diff --git a/.cursorrules b/.cursorrules new file mode 100644 index 000000000..61a306ff0 --- /dev/null +++ b/.cursorrules @@ -0,0 +1,48 @@ +Code Style and Structure: +- Write concise, technical TypeScript code with accurate examples +- Use functional and declarative programming patterns; avoid classes +- Prefer iteration and modularization over code duplication +- Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError) +- Structure files: exported component, subcomponents, helpers, static content, types + +Naming Conventions: +- Use lowercase with dashes for directories (e.g., components/auth-wizard) +- Favor named exports for components + +TypeScript Usage: +- Use TypeScript for all code; prefer interfaces over types +- Avoid enums; use maps instead +- Use functional components with TypeScript interfaces + +Syntax and Formatting: +- Use the "function" keyword for pure functions +- Avoid unnecessary curly braces in conditionals; use concise syntax for simple statements +- Use declarative JSX + +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 + +Performance Optimization: +- Minimize 'use client', 'useEffect', and 'setState'; favor React Server Components (RSC) +- Wrap client components in Suspense with fallback +- Use dynamic loading for non-critical components +- Optimize images: use WebP format, include size data, implement lazy loading + +Key Conventions: +- Use 'nuqs' for URL search parameter state management +- Optimize Web Vitals (LCP, CLS, FID) +- Limit 'use client': + - Favor server components and Next.js SSR + - Use only for Web API access in small components + - Avoid for data fetching or state management + +Follow Next.js docs for Data Fetching, Rendering, and Routing \ No newline at end of file diff --git a/.env.example b/.env.example index ed77d048a..559684160 100644 --- a/.env.example +++ b/.env.example @@ -27,6 +27,8 @@ NEXT_PRIVATE_OIDC_SKIP_VERIFY="" # [[URLS]] NEXT_PUBLIC_WEBAPP_URL="http://localhost:3000" NEXT_PUBLIC_MARKETING_URL="http://localhost:3001" +# URL used by the web app to request itself (e.g. local background jobs) +NEXT_PRIVATE_INTERNAL_WEBAPP_URL="http://localhost:3000" # [[DATABASE]] NEXT_PRIVATE_DATABASE_URL="postgres://documenso:password@127.0.0.1:54320/documenso" @@ -91,6 +93,8 @@ NEXT_PRIVATE_SMTP_UNSAFE_IGNORE_TLS= NEXT_PRIVATE_SMTP_FROM_NAME="Documenso" # REQUIRED: Defines the email address to use as the from address. NEXT_PRIVATE_SMTP_FROM_ADDRESS="noreply@documenso.com" +# OPTIONAL: Defines the service for nodemailer +NEXT_PRIVATE_SMTP_SERVICE= # OPTIONAL: The API key to use for Resend.com NEXT_PRIVATE_RESEND_API_KEY= # OPTIONAL: The API key to use for MailChannels. diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 22705c2d6..baa4c1f07 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -32,6 +32,9 @@ jobs: - name: Run Playwright tests run: npm run ci + env: + # Needed since we use next start which will set the NODE_ENV to production + NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH: './example/cert.p12' - uses: actions/upload-artifact@v4 if: always() diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f69ddb57b..5515b37a6 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -89,22 +89,35 @@ jobs: APP_VERSION="$(git name-rev --tags --name-only $(git rev-parse HEAD) | head -n 1 | sed 's/\^0//')" GIT_SHA="$(git rev-parse HEAD)" - docker manifest create \ - documenso/documenso:latest \ - --amend documenso/documenso-amd64:latest \ - --amend documenso/documenso-arm64:latest \ + # Check if the version is stable (no rc or beta in the version) + if [[ "$APP_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + docker manifest create \ + documenso/documenso:latest \ + --amend documenso/documenso-amd64:latest \ + --amend documenso/documenso-arm64:latest + + docker manifest push documenso/documenso:latest + fi + + if [[ "$APP_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+-rc\.[0-9]+$ ]]; then + docker manifest create \ + documenso/documenso:rc \ + --amend documenso/documenso-amd64:rc \ + --amend documenso/documenso-arm64:rc + + docker manifest push documenso/documenso:rc + fi docker manifest create \ documenso/documenso:$GIT_SHA \ --amend documenso/documenso-amd64:$GIT_SHA \ - --amend documenso/documenso-arm64:$GIT_SHA \ + --amend documenso/documenso-arm64:$GIT_SHA docker manifest create \ documenso/documenso:$APP_VERSION \ --amend documenso/documenso-amd64:$APP_VERSION \ - --amend documenso/documenso-arm64:$APP_VERSION \ + --amend documenso/documenso-arm64:$APP_VERSION - docker manifest push documenso/documenso:latest docker manifest push documenso/documenso:$GIT_SHA docker manifest push documenso/documenso:$APP_VERSION @@ -113,21 +126,34 @@ jobs: APP_VERSION="$(git name-rev --tags --name-only $(git rev-parse HEAD) | head -n 1 | sed 's/\^0//')" GIT_SHA="$(git rev-parse HEAD)" - docker manifest create \ - ghcr.io/documenso/documenso:latest \ - --amend ghcr.io/documenso/documenso-amd64:latest \ - --amend ghcr.io/documenso/documenso-arm64:latest \ + # Check if the version is stable (no rc or beta in the version) + if [[ "$APP_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + docker manifest create \ + ghcr.io/documenso/documenso:latest \ + --amend ghcr.io/documenso/documenso-amd64:latest \ + --amend ghcr.io/documenso/documenso-arm64:latest + + docker manifest push ghcr.io/documenso/documenso:latest + fi + + if [[ "$APP_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+-rc\.[0-9]+$ ]]; then + docker manifest create \ + ghcr.io/documenso/documenso:rc \ + --amend ghcr.io/documenso/documenso-amd64:rc \ + --amend ghcr.io/documenso/documenso-arm64:rc + + docker manifest push ghcr.io/documenso/documenso:rc + fi docker manifest create \ ghcr.io/documenso/documenso:$GIT_SHA \ --amend ghcr.io/documenso/documenso-amd64:$GIT_SHA \ - --amend ghcr.io/documenso/documenso-arm64:$GIT_SHA \ + --amend ghcr.io/documenso/documenso-arm64:$GIT_SHA docker manifest create \ ghcr.io/documenso/documenso:$APP_VERSION \ --amend ghcr.io/documenso/documenso-amd64:$APP_VERSION \ - --amend ghcr.io/documenso/documenso-arm64:$APP_VERSION \ + --amend ghcr.io/documenso/documenso-arm64:$APP_VERSION - docker manifest push ghcr.io/documenso/documenso:latest docker manifest push ghcr.io/documenso/documenso:$GIT_SHA docker manifest push ghcr.io/documenso/documenso:$APP_VERSION diff --git a/.github/workflows/translations-extract.yml b/.github/workflows/translations-extract.yml deleted file mode 100644 index 7f1262cfc..000000000 --- a/.github/workflows/translations-extract.yml +++ /dev/null @@ -1,38 +0,0 @@ -# Extract and compile translations for all PRs. - -name: 'Extract and compile translations' - -on: - workflow_call: - pull_request: - branches: ['main'] - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - extract_translations: - name: Extract and compile translations - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.ref }} - - - uses: ./.github/actions/node-install - - - name: Extract and compile translations - run: | - npm run translate:extract - npm run translate:compile - - - name: Check and commit any files created - run: | - git config --global user.name 'github-actions' - git config --global user.email 'github-actions@documenso.com' - git add packages/lib/translations - git diff --staged --quiet --exit-code || (git commit -m "chore: extract translations" && git push) diff --git a/.github/workflows/translations-upload.yml b/.github/workflows/translations-upload.yml index d40a3217a..cb69d6338 100644 --- a/.github/workflows/translations-upload.yml +++ b/.github/workflows/translations-upload.yml @@ -21,14 +21,12 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: - ref: ${{ github.event.pull_request.head.ref }} + token: ${{ secrets.GH_PAT }} - uses: ./.github/actions/node-install - - name: Extract and compile translations - run: | - npm run translate:extract - npm run translate:compile + - name: Extract translations + run: npm run translate:extract - name: Check and commit any files created run: | diff --git a/.husky/pre-commit b/.husky/pre-commit index cacfc7e37..3d805e3cf 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -13,9 +13,4 @@ node "$MONOREPO_ROOT/scripts/copy-wellknown.cjs" git add "$MONOREPO_ROOT/apps/web/public/" git add "$MONOREPO_ROOT/apps/marketing/public/" -echo "Extract and compile translations" -npm run translate:extract -npm run translate:compile -git add "$MONOREPO_ROOT/packages/lib/translations/" - npx lint-staged diff --git a/apps/documentation/pages/developers/contributing/_meta.json b/apps/documentation/pages/developers/contributing/_meta.json new file mode 100644 index 000000000..262c5231c --- /dev/null +++ b/apps/documentation/pages/developers/contributing/_meta.json @@ -0,0 +1,4 @@ +{ + "index": "Getting Started", + "contributing-translations": "Contributing Translations" +} \ No newline at end of file diff --git a/apps/documentation/pages/developers/contributing/contributing-translations.mdx b/apps/documentation/pages/developers/contributing/contributing-translations.mdx new file mode 100644 index 000000000..b944aa858 --- /dev/null +++ b/apps/documentation/pages/developers/contributing/contributing-translations.mdx @@ -0,0 +1,69 @@ +--- +title: Contributing Translations +description: Learn how to contribute translations to Documenso and become part of our community. +--- + +import { Callout, Steps } from 'nextra/components'; + +# Contributing Translations + +We are always open for help with translations! Currently we utilise AI to generate the initial translations for new languages, which are then improved over time by our awesome community. + +If you are looking for development notes on translations, you can find them [here](/developers/local-development/translations). + + + Contributions are made through GitHub Pull Requests, so you will need a GitHub account to + contribute. + + +## Overview + +We store our translations in PO files, which are located in our GitHub repository [here](https://github.com/documenso/documenso/tree/main/packages/lib/translations). + +The translation files are organized into folders represented by their respective language codes (`en` for English, `de` for German, etc). Each language folder contains three PO files: + +1. `web.po`: Translations for the web application +2. `marketing.po`: Translations for the marketing application +3. `common.po`: Shared translations between web and marketing + +Each PO file contains translations which look like this: + +```po +#: apps/web/src/app/(signing)/sign/[token]/no-longer-available.tsx:61 +msgid "Want to send slick signing links like this one? <0>Check out Documenso." +msgstr "Möchten Sie auffällige Signatur-Links wie diesen senden? <0>Überprüfen Sie Documenso." +``` + +- `msgid`: The original text in English (never edit this manually) +- `msgstr`: The translated text in the target language + + + Notice the `<0>` tags? These represent HTML elements and must remain in both the `msgid` and `msgstr`. Make sure to translate the content between these tags while keeping the tags intact. + + +## How to Contribute + +### Updating Existing Translations + +1. Fork the repository. +2. Navigate to the appropriate language folder. +3. Open the PO file you want to update (web.po, marketing.po, or common.po). +4. Make your changes, ensuring you follow the PO file format. +5. Commit your changes with a message such as `chore: update German translations` +6. Create a Pull Request. + +### Adding a New Language + +If you want to add translations for a language that doesn't exist yet: + +1. Create an issue in our GitHub repository requesting the addition of the new language. +2. Wait for our team to review and approve the request. +3. Once approved, we will set up the necessary files and kickstart the translations with AI to provide initial coverage. + +## Need Help? + + + If you have any questions, hop into our [Discord](https://documen.so/discord) and ask us directly! + + +Thank you for helping make Documenso more accessible to users around the world! diff --git a/apps/documentation/pages/developers/contributing.mdx b/apps/documentation/pages/developers/contributing/index.mdx similarity index 99% rename from apps/documentation/pages/developers/contributing.mdx rename to apps/documentation/pages/developers/contributing/index.mdx index b63f3e9cf..ce690f66f 100644 --- a/apps/documentation/pages/developers/contributing.mdx +++ b/apps/documentation/pages/developers/contributing/index.mdx @@ -1,5 +1,5 @@ --- -title: Contributing Guide +title: Getting started description: Learn how to contribute to Documenso and become part of our community. --- diff --git a/apps/documentation/pages/developers/local-development/signing-certificate.mdx b/apps/documentation/pages/developers/local-development/signing-certificate.mdx index c06fe9440..b8c5b4812 100644 --- a/apps/documentation/pages/developers/local-development/signing-certificate.mdx +++ b/apps/documentation/pages/developers/local-development/signing-certificate.mdx @@ -11,6 +11,10 @@ Digitally signing documents requires a signing certificate in `.p12` format. You Follow the steps below to create a free, self-signed certificate for local development. + + These steps should be run on a UNIX based system, otherwise you may run into an error. + + ### Generate Private Key @@ -38,11 +42,17 @@ You will be prompted to enter some information, such as the certificate's Common Combine the private key and the self-signed certificate to create a `.p12` certificate. Use the following command: ```bash -openssl pkcs12 -export -out certificate.p12 -inkey private.key -in certificate.crt +openssl pkcs12 -export -out certificate.p12 -inkey private.key -in certificate.crt -legacy ``` - If you get the error "Error: Failed to get private key bags", add the `-legacy` flag to the command `openssl pkcs12 -export -out certificate.p12 -inkey private.key -in certificate.crt -legacy`. +When running the application in Docker, you may encounter permission issues when attempting to sign documents using your certificate (.p12) file. This happens because the application runs as a non-root user inside the container and needs read access to the certificate. + +To resolve this, you'll need to update the certificate file permissions to allow the container user 1001, which runs NextJS, to read it: + +```bash +sudo chown 1001 certificate.p12 +``` @@ -54,8 +64,8 @@ Note that for local development, the password can be left empty. ### Add Certificate to the Project -Finally, add the certificate to the project. Place the `certificate.p12` file in the `/apps/web/resources` directory. If the directory doesn't exist, create it. +Use the `NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH` environment variable to point at the certificate you created. -The final file path should be `/apps/web/resources/certificate.p12`. +Details about environment variables associated with certificates can be found [here](/developers/self-hosting/signing-certificate#configure-documenso-to-use-the-certificate). diff --git a/apps/documentation/pages/developers/public-api/reference.mdx b/apps/documentation/pages/developers/public-api/reference.mdx new file mode 100644 index 000000000..648ee8a34 --- /dev/null +++ b/apps/documentation/pages/developers/public-api/reference.mdx @@ -0,0 +1,507 @@ +--- +title: API Reference +description: Reference documentation for the Documenso public API. +--- + +import { Callout, Steps } from 'nextra/components'; + +# API Reference + +The Swagger UI for the API is available at [/api/v1/openapi](https://app.documenso.com/api/v1/openapi). This page provides detailed information about the API endpoints, request and response formats, and authentication requirements. + +## Upload a Document + +Uploading a document to your Documenso account requires a two-step process. + + + +### Create Document + +First, you need to make a `POST` request to the `/api/v1/documents` endpoint, which takes a JSON payload with the following fields: + +```json +{ + "title": "string", + "externalId": "string", + "recipients": [ + { + "name": "string", + "email": "user@example.com", + "role": "SIGNER", + "signingOrder": 0 + } + ], + "meta": { + "subject": "string", + "message": "string", + "timezone": "Etc/UTC", + "dateFormat": "yyyy-MM-dd hh:mm a", + "redirectUrl": "string", + "signingOrder": "PARALLEL" + }, + "authOptions": { + "globalAccessAuth": "ACCOUNT", + "globalActionAuth": "ACCOUNT" + }, + "formValues": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string" + } +} +``` + +- `title` _(required)_ - This represents the document's title. +- `externalId` - This is an optional field that you can use to store an external identifier for the document. This can be useful for tracking the document in your system. +- `recipients` _(required)_ - This is an array of recipient objects. Each recipient object has the following fields: + - `name` - The name of the recipient. + - `email` - The email address of the recipient. + - `role` - The role of the recipient. See the [available roles](/users/signing-documents#roles). + - `signingOrder` - The order in which the recipient should sign the document. This is an integer value starting from 0. +- `meta` - This object contains additional metadata for the document. It has the following fields: + - `subject` - The subject of the email that will be sent to the recipients. + - `message` - The message of the email that will be sent to the recipients. + - `timezone` - The timezone in which the document should be signed. + - `dateFormat` - The date format that should be used in the document. + - `redirectUrl` - The URL to which the user should be redirected after signing the document. + - `signingOrder` - The signing order for the document. This can be either `SEQUENTIAL` or `PARALLEL`. +- `authOptions` - This object contains authentication options for the document. It has the following fields: + - `globalAccessAuth` - The authentication level required to access the document. This can be either `ACCOUNT` or `null`. + - If the document is set to `ACCOUNT`, all recipients must authenticate with their Documenso account to access it. + - The document can be accessed without a Documenso account if it's set to `null`. + - `globalActionAuth` - The authentication level required to perform actions on the document. This can be `ACCOUNT`, `PASSKEY`, `TWO_FACTOR_AUTH`, or `null`. + - If the document is set to `ACCOUNT`, all recipients must authenticate with their Documenso account to perform actions on the document. + - If it's set to `PASSKEY`, all recipients must have the passkey active to perform actions on the document. + - If it's set to `TWO_FACTOR_AUTH`, all recipients must have the two-factor authentication active to perform actions on the document. + - If it's set to `null`, all the recipients can perform actions on the document without any authentication. +- `formValues` - This object contains additional form values for the document. This property only works with native PDF fields and accepts three types: number, text and boolean. + + + The `globalActionAuth` property is only available for Enterprise accounts. + + +Here's an example of the JSON payload for uploading a document: + +```json +{ + "title": "my-document.pdf", + "externalId": "12345", + "recipients": [ + { + "name": "Alex Blake", + "email": "alexblake@email.com", + "role": "SIGNER", + "signingOrder": 1 + }, + { + "name": "Ash Drew", + "email": "ashdrew@email.com", + "role": "SIGNER", + "signingOrder": 0 + } + ], + "meta": { + "subject": "Sign the document", + "message": "Hey there, please sign this document.", + "timezone": "Europe/London", + "dateFormat": "Day, Month Year", + "redirectUrl": "https://mysite.com/welcome", + "signingOrder": "SEQUENTIAL" + }, + "authOptions": { + "globalAccessAuth": "ACCOUNT", + "globalActionAuth": "PASSKEY" + } +} +``` + +### Upload to S3 + +A successful API call to the `/api/v1/documents` endpoint returns a JSON response containing the upload URL, document ID, and recipient information. + +The upload URL is a pre-signed S3 URL that you can use to upload the document to the Documenso (or your) S3 bucket. You need to make a `PUT` request to this URL to upload the document. + +```json +{ + "uploadUrl": "https://///my-document.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=&X-Amz-Date=&X-Amz-Expires=3600&X-Amz-Signature=&X-Amz-SignedHeaders=host&x-id=PutObject", + "documentId": 51, + "recipients": [ + { + "recipientId": 11, + "name": "Alex Blake", + "email": "alexblake@email.com", + "token": "", + "role": "SIGNER", + "signingOrder": 1, + "signingUrl": "https://app.documenso.com/sign/" + }, + { + "recipientId": 12, + "name": "Ash Drew", + "email": "ashdrew@email.com", + "token": "", + "role": "SIGNER", + "signingOrder": 0, + "signingUrl": "https://app.documenso.com/sign/" + } + ] +} +``` + +When you make the `PUT` request to the pre-signed URL, you need to include the document file you want to upload. The image below shows how to upload a document to the S3 bucket via Postman. + +![Upload document to S3](/api-reference/upload-document-to-s3.webp) + +Here's an example of how to upload a document using cURL: + +```bash +curl --location --request PUT 'https://///my-document.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=&X-Amz-Date=&X-Amz-Expires=3600&X-Amz-Signature=&X-Amz-SignedHeaders=host&x-id=PutObject' \ +--form '=@"/Users/my-user/Documents/documenso.pdf"' +``` + +Once the document is successfully uploaded, you can access it in your Documenso account dashboard. The screenshot below shows the document that was uploaded via the API. + +![Uploaded Document](/api-reference/document-uploaded-to-documenso-via-api.webp) + + + +## Generate Document From Template + +Documenso allows you to generate documents from templates. This is useful when you have a standard document format you want to reuse. + +The API endpoint for generating a document from a template is `/api/v1/templates/{templateId}/generate-document`, and it takes a JSON payload with the following fields: + +```json +{ + "title": "string", + "externalId": "string", + "recipients": [ + { + "id": 0, + "name": "string", + "email": "user@example.com", + "signingOrder": 0 + } + ], + "meta": { + "subject": "string", + "message": "string", + "timezone": "string", + "dateFormat": "string", + "redirectUrl": "string", + "signingOrder": "PARALLEL" + }, + "authOptions": { + "globalAccessAuth": "ACCOUNT", + "globalActionAuth": "ACCOUNT" + }, + "formValues": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string" + } +} +``` + +The JSON payload is identical to the payload for uploading a document, so you can read more about the fields in the [Create Document](/developers/public-api/reference#create-document) step. For this API endpoint, the `recipients` property is required. + + + +### Grab the Template ID + +The first step is to retrieve the template ID from the Documenso dashboard. You can find the template ID in the URL by navigating to the template details page. + +![Template ID](/api-reference/documenso-template-id.webp) + +In this case, the template ID is "99999". + +### Retrieve the Recipient(s) ID(s) + +Once you have the template ID, the next step involves retrieving the ID(s) of the recipient(s) from the template. You can do this by making a GET request to `/api/v1/templates/{template-id}`. + +A successful response looks as follows: + +```json +{ + "id": 0, + "externalId": "string", + "type": "PUBLIC", + "title": "string", + "userId": 0, + "teamId": 0, + "templateDocumentDataId": "string", + "createdAt": "2024-10-11T08:46:58.247Z", + "updatedAt": "2024-10-11T08:46:58.247Z", + "templateMeta": { + "id": "string", + "subject": "string", + "message": "string", + "timezone": "string", + "dateFormat": "string", + "templateId": 0, + "redirectUrl": "string", + "signingOrder": "PARALLEL" + }, + "directLink": { + "token": "string", + "enabled": true + }, + "templateDocumentData": { + "id": "string", + "type": "S3_PATH", + "data": "string" + }, + "Field": [ + { + "id": 0, + "recipientId": 0, + "type": "SIGNATURE", + "page": 0, + "positionX": "string", + "positionY": "string", + "width": "string", + "height": "string" + } + ], + "Recipient": [ + { + "id": 0, + "email": "user@example.com", + "name": "string", + "signingOrder": 0, + "authOptions": "string", + "role": "CC" + } + ] +} +``` + +You'll need the recipient(s) ID(s) for the next step. + +### Generate the Document + +To generate a document from the template, you need to make a POST request to the `/api/v1/templates/{template-id}/generate-document` endpoint. + +At the minimum, you must provide the `recipients` array in the JSON payload. Here's an example of the JSON payload: + +```json +{ + "recipients": [ + { + "id": 0, + "name": "Ash Drew", + "email": "ashdrew@email.com", + "signingOrder": 0 + } + ] +} +``` + +Filling the `recipients` array with the corresponding recipient for each template placeholder recipient is recommended. For example, if the template has two placeholders, you should provide at least two recipients in the `recipients` array. Otherwise, the document will be sent to inexistent recipients such as ``. However, the recipients can always be edited via the API or the web app. + +A successful response will contain the document ID and recipient(s) information. + +```json +{ + "documentId": 999, + "recipients": [ + { + "recipientId": 0, + "name": "Ash Drew", + "email": "ashdrew@email.com", + "token": "", + "role": "SIGNER", + "signingOrder": null, + "signingUrl": "https://app.documenso.com/sign/" + } + ] +} +``` + +You can now access the document in your Documenso account dashboard. The screenshot below shows the document that was generated from the template. + +![Generated Document](/api-reference/document-generated-from-template.webp) + + + +## Add Fields to Document + +The API allows you to add fields to a document via the `/api/v1/documents/{documentId}/fields` endpoint. This is useful when you want to add fields to a document before sending it to recipients. + +To add fields to a document, you need to make a `POST` request with a JSON payload containing the field(s) information. + +```json +{ + "recipientId": 0, + "type": "SIGNATURE", + "pageNumber": 0, + "pageX": 0, + "pageY": 0, + "pageWidth": 0, + "pageHeight": 0, + "fieldMeta": { + "label": "string", + "placeholder": "string", + "required": true, + "readOnly": true, + "type": "text", + "text": "string", + "characterLimit": 0 + } +} + +// or + +[ + { + "recipientId": 0, + "type": "SIGNATURE", + "pageNumber": 0, + "pageX": 0, + "pageY": 0, + "pageWidth": 0, + "pageHeight": 0 + }, + { + "recipientId": 0, + "type": "TEXT", + "pageNumber": 0, + "pageX": 0, + "pageY": 0, + "pageWidth": 0, + "pageHeight": 0, + "fieldMeta": { + "label": "string", + "placeholder": "string", + "required": true, + "readOnly": true, + "type": "text", + "text": "string", + "characterLimit": 0 + } + } +] +``` + +This endpoint accepts either one field or an array of fields. + +Before adding fields to a document, you need each recipient's ID. If the document already has recipients, you can query the document to retrieve the recipient's details. If the document has no recipients, you need to add a recipient via the UI or API before adding a field. + + + +### Retrieve the Recipient(s) ID(s) + +Perform a `GET` request to the `/api/v1/documents/{id}` to retrieve the details of a specific document, including the recipient's information. + +An example response would look like this: + +```json +{ + "id": 137, + "externalId": null, + "userId": 3, + "teamId": null, + "title": "documenso.pdf", + "status": "DRAFT", + "documentDataId": "", + "createdAt": "2024-10-11T12:29:12.725Z", + "updatedAt": "2024-10-11T12:29:12.725Z", + "completedAt": null, + "recipients": [ + { + "id": 55, + "documentId": 137, + "email": "ashdrew@email.com", + "name": "Ash Drew", + "role": "SIGNER", + "signingOrder": null, + "token": "", + "signedAt": null, + "readStatus": "NOT_OPENED", + "signingStatus": "NOT_SIGNED", + "sendStatus": "NOT_SENT", + "signingUrl": "https://app.documenso.com/sign/" + } + ] +} +``` + +From this response, you'll only need the recipient ID, which is `55` in this case. + +### (OR) Add a Recipient + +If the document doesn't already have recipient(s), you can add recipient(s) via the API. Make a `POST` request to the `/api/v1/documents/{documentId}/recipients` endpoint with the recipient information. This endpoint takes the following JSON payload: + +```json +{ + "name": "string", + "email": "user@example.com", + "role": "SIGNER", + "signingOrder": 0, + "authOptions": { + "actionAuth": "ACCOUNT" + } +} +``` + +The `authOptions` property is only available for Enterprise accounts. + +Here's an example of the JSON payload for adding a recipient: + +```json +{ + "name": "Ash Drew", + "email": "ashdrew@email.com", + "role": "SIGNER", + "signingOrder": 0 +} +``` + +A successful request will return a JSON response with the newly added recipient. You can now use the recipient ID to add fields to the document. + +### Add Field(s) + +Now you can make a `POST` request to the `/api/v1/documents/{documentId}/fields` endpoint with the field(s) information. Here's an example: + +```json +[ + { + "recipientId": 55, + "type": "SIGNATURE", + "pageNumber": 1, + "pageX": 50, + "pageY": 20, + "pageWidth": 25, + "pageHeight": 5 + }, + { + "recipientId": 55, + "type": "TEXT", + "pageNumber": 1, + "pageX": 20, + "pageY": 50, + "pageWidth": 30, + "pageHeight": 7.5, + "fieldMeta": { + "label": "Address", + "placeholder": "32 New York Street, 41241", + "required": true, + "readOnly": false, + "type": "text", + "text": "32 New York Street, 41241", + "characterLimit": 40 + } + } +] +``` + + + The `text` field represents the default value of the field. If the user doesn't provide any other + value, this is the value that will be used to sign the field. + + +A successful request will return a JSON response with the newly added fields. The image below illustrates the fields added to the document via the API. + +![A screenshot of the document in the Documenso editor](/api-reference/fields-added-via-api.webp) + + diff --git a/apps/documentation/pages/developers/self-hosting/how-to.mdx b/apps/documentation/pages/developers/self-hosting/how-to.mdx index 2acbf9abb..0d1583859 100644 --- a/apps/documentation/pages/developers/self-hosting/how-to.mdx +++ b/apps/documentation/pages/developers/self-hosting/how-to.mdx @@ -5,6 +5,8 @@ description: Learn how to self-host Documenso on your server or cloud infrastruc import { Callout, Steps } from 'nextra/components'; +import { CallToAction } from '@documenso/ui/components/call-to-action'; + # Self Hosting We support various deployment methods and are actively working on adding more. Please let us know if you have a specific deployment method in mind! @@ -131,7 +133,7 @@ volumes: After updating the volume binding, save the `compose.yml` file and run the following command to start the containers: ```bash -docker-compose --env-file ./.env -d up +docker-compose --env-file ./.env up -d ``` The command will start the PostgreSQL database and the Documenso application containers. @@ -273,3 +275,5 @@ We offer several alternative deployment methods for Documenso if you need more o ## Koyeb [![Deploy to Koyeb](https://www.koyeb.com/static/images/deploy/button.svg)](https://app.koyeb.com/deploy?type=git&repository=github.com/documenso/documenso&branch=main&name=documenso-app&builder=dockerfile&dockerfile=/docker/Dockerfile) + + diff --git a/apps/documentation/pages/developers/self-hosting/index.mdx b/apps/documentation/pages/developers/self-hosting/index.mdx index 84cbed30b..b4aefb848 100644 --- a/apps/documentation/pages/developers/self-hosting/index.mdx +++ b/apps/documentation/pages/developers/self-hosting/index.mdx @@ -3,6 +3,10 @@ title: Getting Started with Self-Hosting description: A step-by-step guide to setting up and hosting your own Documenso instance. --- +import { CallToAction } from '@documenso/ui/components/call-to-action'; + # Getting Started with Self-Hosting This is a step-by-step guide to setting up and hosting your own Documenso instance. Before getting started, [select the right license for you](/users/licenses). + + diff --git a/apps/documentation/pages/users/_meta.json b/apps/documentation/pages/users/_meta.json index 3ccb8e7c6..53733ea63 100644 --- a/apps/documentation/pages/users/_meta.json +++ b/apps/documentation/pages/users/_meta.json @@ -10,6 +10,8 @@ "signing-documents": "Signing Documents", "templates": "Templates", "direct-links": "Direct Signing Links", + "document-visibility": "Document Visibility", + "teams": "Teams", "-- Legal Overview": { "type": "separator", "title": "Legal Overview" diff --git a/apps/documentation/pages/users/teams/_meta.json b/apps/documentation/pages/users/teams/_meta.json new file mode 100644 index 000000000..b9548a39b --- /dev/null +++ b/apps/documentation/pages/users/teams/_meta.json @@ -0,0 +1,5 @@ +{ + "general-settings": "General Settings", + "document-visibility": "Document Visibility", + "sender-details": "Email Sender Details" +} diff --git a/apps/documentation/pages/users/teams/document-visibility.mdx b/apps/documentation/pages/users/teams/document-visibility.mdx new file mode 100644 index 000000000..8d2f82266 --- /dev/null +++ b/apps/documentation/pages/users/teams/document-visibility.mdx @@ -0,0 +1,45 @@ +--- +title: Document Visibility +description: Learn how to control the visibility of your team documents. +--- + +import { Callout } from 'nextra/components'; + +# Team's Document Visibility + +The default document visibility option allows you to control who can view and access the documents uploaded to your team account. The document visibility can be set to one of the following options: + +- **Everyone** - The document is visible to all team members. +- **Managers and above** - The document is visible to team members with the role of _Manager or above_ and _Admin_. +- **Admin only** - The document is only visible to the team's admins. + +![A screenshot of the document visibility selector from the team's general settings page](/teams/team-general-settings-document-visibility-select.webp) + +The default document visibility is set to "_EVERYONE_" by default. You can change this setting by going to the [team's general settings page](/users/teams/general-settings) and selecting a different visibility option. + + + If the team member uploading the document has a role lower than the default document visibility, + the document visibility will be set to a lower visibility level matching the team member's role. + + +Here's how it works: + +- If a user with the "_Member_" role creates a document and the default document visibility is set to "_Admin_" or "_Managers and above_", the document's visibility is set to "_Everyone_". +- If a user with the "_Manager_" role creates a document and the default document visibility is set to "_Admin_", the document's visibility is set to "_Managers and above_". +- Otherwise, the document's visibility is set to the default document visibility. + +You can change the visibility of a document at any time by editing the document and selecting a different visibility option. + +![A screenshot of the Documenso's document editor page where you can update the document visibility](/teams/document-visibility-settings.webp) + + + Updating the default document visibility in the team's general settings will not affect the + visibility of existing documents. You will need to update the visibility of each document + individually. + + +## A Note on Document Access + +The `document owner` (the user who created the document) always has access to the document, regardless of the document's visibility settings. This means that even if a document is set to "Admins only", the document owner can still view and edit the document. + +The `recipient` (the user who receives the document for signature, approval, etc.) also has access to the document, regardless of the document's visibility settings. This means that even if a document is set to "Admins only", the recipient can still view and sign the document. diff --git a/apps/documentation/pages/users/teams/general-settings.mdx b/apps/documentation/pages/users/teams/general-settings.mdx new file mode 100644 index 000000000..e10d379b0 --- /dev/null +++ b/apps/documentation/pages/users/teams/general-settings.mdx @@ -0,0 +1,15 @@ +--- +title: General Settings +description: Learn how to manage your team's General settings. +--- + +# General Settings + +You can manage your team's general settings by clicking on the **General Settings** tab in the team's settings dashboard. + +![A screenshot of team's General settings page](/teams/team-general-settings.webp) + +The general settings page allows you to update the following settings: + +- **Document Visibility** - Set the default visibility of the documents created by team members. Learn more about [document visibility](/users/teams/document-visibility). +- **Sender Details** - Set whether the sender's name should be included in the emails sent by the team. Learn more about [sender details](/users/teams/sender-details). diff --git a/apps/documentation/pages/users/teams/sender-details.mdx b/apps/documentation/pages/users/teams/sender-details.mdx new file mode 100644 index 000000000..196cd22e7 --- /dev/null +++ b/apps/documentation/pages/users/teams/sender-details.mdx @@ -0,0 +1,14 @@ +--- +title: Email Sender Details +description: Learn how to update the sender details for your team's email notifications. +--- + +## Sender Details + +If the **Sender Details** setting is enabled, the emails sent by the team will include the sender's name. The email will say: + +> "Example User" on behalf of "Example Team" has invited you to sign "document.pdf" + +If the **Sender Details** setting is disabled, the emails sent by the team will not include the sender's name. The email will say: + +> "Example Team" has invited you to sign "document.pdf" diff --git a/apps/documentation/public/api-reference/documenso-template-id.webp b/apps/documentation/public/api-reference/documenso-template-id.webp new file mode 100644 index 000000000..b3a7af0b4 Binary files /dev/null and b/apps/documentation/public/api-reference/documenso-template-id.webp differ diff --git a/apps/documentation/public/api-reference/document-generated-from-template.webp b/apps/documentation/public/api-reference/document-generated-from-template.webp new file mode 100644 index 000000000..d203380f7 Binary files /dev/null and b/apps/documentation/public/api-reference/document-generated-from-template.webp differ diff --git a/apps/documentation/public/api-reference/document-uploaded-to-documenso-via-api.webp b/apps/documentation/public/api-reference/document-uploaded-to-documenso-via-api.webp new file mode 100644 index 000000000..2b08723a8 Binary files /dev/null and b/apps/documentation/public/api-reference/document-uploaded-to-documenso-via-api.webp differ diff --git a/apps/documentation/public/api-reference/fields-added-via-api.webp b/apps/documentation/public/api-reference/fields-added-via-api.webp new file mode 100644 index 000000000..f3aeaf334 Binary files /dev/null and b/apps/documentation/public/api-reference/fields-added-via-api.webp differ diff --git a/apps/documentation/public/api-reference/upload-document-to-s3.webp b/apps/documentation/public/api-reference/upload-document-to-s3.webp new file mode 100644 index 000000000..e5367c4d5 Binary files /dev/null and b/apps/documentation/public/api-reference/upload-document-to-s3.webp differ diff --git a/apps/documentation/public/teams/document-visibility-settings.webp b/apps/documentation/public/teams/document-visibility-settings.webp new file mode 100644 index 000000000..7022a3584 Binary files /dev/null and b/apps/documentation/public/teams/document-visibility-settings.webp differ diff --git a/apps/documentation/public/teams/team-general-settings-document-visibility-select.webp b/apps/documentation/public/teams/team-general-settings-document-visibility-select.webp new file mode 100644 index 000000000..ef312eeeb Binary files /dev/null and b/apps/documentation/public/teams/team-general-settings-document-visibility-select.webp differ diff --git a/apps/documentation/public/teams/team-general-settings.webp b/apps/documentation/public/teams/team-general-settings.webp new file mode 100644 index 000000000..3b5607e6a Binary files /dev/null and b/apps/documentation/public/teams/team-general-settings.webp differ diff --git a/apps/marketing/content/blog/cal.com-chooses-documenso-for-dpa-and-baa-scalability-and-compliance.mdx b/apps/marketing/content/blog/cal.com-chooses-documenso-for-dpa-and-baa-scalability-and-compliance.mdx new file mode 100644 index 000000000..37e41e7b0 --- /dev/null +++ b/apps/marketing/content/blog/cal.com-chooses-documenso-for-dpa-and-baa-scalability-and-compliance.mdx @@ -0,0 +1,82 @@ +--- +title: Cal.com Chooses Documenso for DPA and BAA Scalability and Compliance +description: Learn how Cal.com scales their Data Processing Agreement (DPA) and Business Associate Agreement (BAA) processes with Documenso’s open source platform as they grow. +authorName: 'Timur Ercan' +authorImage: '/blog/blog-author-timur.jpeg' +authorRole: 'Co-Founder' +date: 2024-10-11 +tags: + - Customer Story + - Open Startup + - Open Source +--- + +
+ + +
+ Scheduling Infrastructure for Everyone. +
+
+TL;DR: Cal.com uses Documenso’s template direct links to facilitate low-friction compliance paperwork, enhancing scalability and user experience. + +## Cal.com – The Most Public Private Company + +[Cal.com](Cal.com) is an open source company that needs no introduction. Founded in 2021 by Bailey Pumfleet and Peer Richelsen, it quickly evolved from an open source alternative to the widespread but limited scheduling platform Calendly into the internet’s most beloved scheduling solution. Starting with just two founders, Cal.com has grown into a team of 22, facilitating millions of bookings per year for its ever-growing user and customer base. + +Their commitment to transparency is evident as they follow the [open startup movement](https://cal.com/open), opening up not only their source code but also providing insights into their business operations. Their Commercial Open Source Software (COSS) model, combining a company and an open source project, has inspired a whole cohort of startups joining the space—not least of which is Documenso. + +## The Need + +At this point, Cal.com serves customers of all sizes, from single users to large enterprises. To provide the best product for their customers, they are certified for SOC 2, HIPAA, GDPR, and many other compliance regulations. One challenge that comes with this is the increasing number of waivers that need to be signed when onboarding customers. Business Associate Agreements (BAAs) and Data Processing Agreements (DPAs) are two of the more commonly known examples. To get these signed with minimal effort for both sides, they were looking for a solution to handle these at scale. + +> We love open source. + +— Peer Richelsen, Co-Founder, Cal.com + +Being an open source company, they also prefer open source in their vendors—for both the shared philosophy and the higher level of trust. The goal was to integrate signing into the checkout process as seamlessly as possible. + +## The Solution + +
+ + +
+ Sign a DPA with Cal by clicking a link anytime. +
+
+ +Documenso offers exactly this solution through direct link templates, enabling Cal.com to: + +- Provide Immediate Access: Customers can access and sign necessary compliance documents through direct links at any time. +- Enhance User Experience: Users are immediately forwarded to onboarding after signing. +- Ensure Easy Access: The documents are stored within the company’s team account, allowing easy access for anyone who needs them. + +Direct Link templates can also easily be embedded, using the [Documenso widget](https://documen.so/embedded). Embedding anywhere, pre-Filling the templates and notfiying the compliance team at certain point of the flow are a few of the many option the team now has in continously enhanceing their onboard and compliance UX. + +Read more about our direct link templates here: [Direct Link Signing](https://docs.documenso.com/users/direct-links). + +## The Journey + +Initially, Cal.com’s team approached the new solution with skepticism. As Bailey reflected: + +> We were intrigued but skeptical at first, as we put a lot of thought into compliance and doing things right. Documenso’s documentation and support showcased how their direct link templates could meet our needs while being highly compliant. + +This experience highlights Documenso’s trust philosophy. We strive to be transparent in everything we do and let people judge for themselves. It also shows that we neither want nor get trust by default. Doing things right is a conversation worth having, especially in a space as opaque as digital signatures. It goes without saying that the whole team is hyped to have Cal.com on board and yet another open source company joining the open signing movement 🚀 + +If you have any questions or comments, please reach out on [Twitter / X](https://twitter.com/eltimuro) (DM open) or [Discord](https://documen.so/discord). + +Thinking about switching to a modern signing platform? Reach out anytime: [https://documen.so/sales](https://documen.so/sales) + +Best from Hamburg\ +Timur diff --git a/apps/marketing/content/blog/customer-story-prisma-4-reasons-prisma-choose-documenso-for-signing.mdx b/apps/marketing/content/blog/customer-story-prisma-4-reasons-prisma-choose-documenso-for-signing.mdx new file mode 100644 index 000000000..096ae234d --- /dev/null +++ b/apps/marketing/content/blog/customer-story-prisma-4-reasons-prisma-choose-documenso-for-signing.mdx @@ -0,0 +1,85 @@ +--- +title: 'Customer Story Prisma: 4 Reasons why Prisma chose Documenso for Signatures' +description: We are happy to welcome Prisma, another OSS company, as a customer. Read here why they choose us. +authorName: 'Timur Ercan' +authorImage: '/blog/blog-author-timur.jpeg' +authorRole: 'Co-Founder' +date: 2024-09-26 +tags: + - Prisma + - Customer Story + - Open Source +--- + +
+ + +
+ Prisma uses Documenso for collaborative team signing. +
+
+ +> TLDR; Prisma is now using Documenso, and [we added visibility scopes](https://docs.documenso.com/users/document-visibility) + +# Prisma + +Prisma is an open-source company known for its modern OSS ORM (Object-Relational Mapping) tools that simplify database interactions for developers. Their flagship product, Prisma ORM, provides a type-safe way to query databases like PostgreSQL, MySQL, and many more. With the addition of Prisma Studio, an intuitive database management interface, Prisma makes it easier and more efficient for developers to work with databases. With their new additions, Prisma Pulse and Accelerate, you can react to real-time database changes and optimize your queries. And they are completely [open source](https://github.com/prisma/prisma)! + +# We choose Prisma too! + +I discovered Prisma when planning the tech stack for the [first version of Documenso](https://github.com/documenso/documenso/releases/tag/0.9-developer-preview). Prisma has felt natural to use since day one and has been the base of our database architecture ever since. It's great to see them develop and grow with us. + +# Why they choose us + +## 1. Signature Flows + +Documenso signing flows are highly configurable, designed to adapt to the needs of any document signing process. Whether you're working with different roles, varying settings, or specific delivery methods, Documenso offers the flexibility to suit your requirements. You can choose to send documents via email, share a manual link, generate a link through the API, or even use a static direct link for quick access—all while ensuring a smooth signing experience. + +Additionally, you can create templates to streamline and reuse common workflows, saving valuable time. Direct link templates enable users to drive the flow themselves, providing a straightforward path for signing. For a seamless experience, Documenso also allows you to embed the signing process directly into your website, ensuring an uninterrupted, integrated workflow tailored to your needs. + +## 2. Modern UX + +
+ + +
+ We call Documenso's design "Happy Minimalism" +
+
+ +We’ve crafted Documenso with a sleek, modern interface that makes it incredibly easy to use. Whether you’re signing documents, managing workflows, or fine-tuning settings, its intuitive design allows you to accomplish tasks quickly and effortlessly. More than just powerful, Documenso is a pleasure to navigate—designed to be accessible to everyone, no matter their level of tech experience. + +## 3. Teams + +### Teamwork Makes the Dream Work + +Documenso makes teamwork a breeze with its team management features. You can easily set up and organize teams, making it simple to share and manage documents and workflows together. This is a lifesaver for larger organizations or teams spread across different departments, ensuring everyone stays in sync and on track. Different visibility scopes ensure private documents stay private and others are shared for easy collaboration. + +### Document Visibility + +Collaboration within a team often demands different levels of access to documents. For instance, the Documenso team at Prisma needed a way to set custom visibility on some documents while keeping others accessible to everyone. To address this need, we introduced role-based visibility scopes. This feature allows teams to manage documents more effectively. They can make certain documents visible only to managers or, in special cases, restricted to admins. This ensures sensitive information stays protected while general documents remain accessible to those who need them. + +Learn more about visibility scopes and [how they can benefit your team here](https://docs.documenso.com/users/document-visibility). + +## 4. OSS! + +As you might know, we are open-source! This means you can peek under the hood, tweak things to your liking, and even contribute to making the platform better. We love the community-driven aspect of open-source, and it aligns perfectly with our goal to keep improving and innovating with input from our users. + +So, whether you're looking to streamline your document workflows or just need a solid, reliable platform, Documenso has got your back. And we're thrilled to serve another OSS company and help make the space more open. + +If you have any questions or comments, please reach out on [Twitter / X](https://twitter.com/eltimuro) (DM open) or [Discord](https://documen.so/discord). + +Thinking about switching to a modern signing platform? Reach out anytime: [https://documen.so/sales](https://documen.so/sales) + +Best from Hamburg\ +Timur diff --git a/apps/marketing/content/blog/go-fork-yourself.mdx b/apps/marketing/content/blog/go-fork-yourself.mdx new file mode 100644 index 000000000..4512d7be5 --- /dev/null +++ b/apps/marketing/content/blog/go-fork-yourself.mdx @@ -0,0 +1,86 @@ +--- +title: Go Fork Yourself +description: Curious about our take on open-source and code forking? Discover why we see forking not as a threat but as a vital part of the Open Source ecosystem. +authorName: 'Timur Ercan' +authorImage: '/blog/blog-author-timur.jpeg' +authorRole: 'Co-Founder' +date: 2024-10-03 +tags: + - Culture + - Open Startup + - Open Source +--- + +> TLDR; At Documenso, we see OSS as co-owned by all. Forking—collaborative or not—is part of the open-source spirit. + +## Freedom vs. Ownership + +Recently, there has been a lot of debate on the subject of forks and the usage of OSS IP (Open Source Software Intellectual Property). While I mostly aim to stay out of these controversies (as there is no “winning”), I wanted to take this opportunity to share my views on IP and forking culture here at Documenso. I don’t presume this is the ideal path, but for me, it’s the only path that makes sense. + +What these issues show foremost, in my opinion, is that the concept of Open Source is still evolving. I have heard many say, “Open Source is clearly defined” and that there is no ambiguity anymore. That may be true on the legal side, but there are vast differences in how these rules are interpreted and lived out. Here are a few questions to illustrate the point: + +1. Is it okay to use an open-source project without ever giving back? +2. Is it okay to fork (some might say copy) an OSS product and build something on top of it? +3. Are we morally obliged to fight those who provide different answers to these questions than we do? + +## Embracing Forks and Collaboration + +Since starting Documenso, I’ve thought a lot about what it actually means to be Open Source for us. So far, it has been about openness in working with everyone, from contributors to customers and sharing our work transparently. For this, we have been richly rewarded with attention and reach. This collaborative give-and-take is what people commonly associate with being Open Source, and it seems ideal. + +Yet, there are the questions mentioned above. And while these may be contentious, my take is straightforward: + +1. Yes. +2. Yes. +3. No. + +I say this because, to me, the principles of Open Source are rooted in freedom and collaboration. That means allowing others to use, improve, or even compete with what you’ve built without feeling possessive over the code. The beauty of Open Source lies in its openness—its ability to be forked, reused, and adapted by anyone. + +You may answer these questions differently for your own reasons. One thing I’ve found lacking in the discourse is the fact that Open Source is still being treated as socially proprietary. If it’s under an open-source license, you can fork it and try to improve upon the original, and there’s nothing wrong with that. The same is true for closed-source startups. Yet in Open Source, there’s a notion that it’s somehow “dirty,” even though the license explicitly allows it. + +## Forking in Action: Real-World Examples + +When the team behind **Node.js** disagreed with its governance and pace of development, they forked the project to create **io.js**. This wasn’t seen as dirty but as a necessary push for change. In fact, the fork resulted in positive changes—better community governance and faster development—which eventually led to the merge of the two projects under the Node.js Foundation. It shows that forking can be a catalyst for improvement, not just competition. + +## The Misconception of “Exploitative” Usage + +However, sometimes forks don’t merge back but still bring positive change. A good example is **Jenkins**, which was forked from **Hudson** over disagreements in governance after Oracle acquired Sun Microsystems. Jenkins quickly overtook Hudson in terms of community support, development, and innovation. Rather than being seen as a hostile move, the fork enabled Jenkins to become a thriving project, better aligned with the open-source ethos of collaboration and transparency. It emphasizes that forking isn’t inherently exploitative; it can simply be a way to realize a project’s full potential. + +And then there’s **MariaDB**, a fork of **MySQL**. After Oracle acquired MySQL, many in the community feared the project’s open-source nature could be compromised. The fork preserved its spirit, and MariaDB has since grown to become a popular and thriving database. It’s a reminder that sometimes, forking is not just acceptable—it’s necessary to uphold the values and freedoms of open-source software. + +
+ + +
+ Funny Meme to drive the point home. +
+
+ +My view is that the code is not “your” code, just as Documenso’s code is not “our” code. It’s been co-owned by the world ever since we published the repo under AGPL V3. That is the whole point. It’s finally not owned by anyone (cue the “everyone/no one” meme). Open Source is for everyone, even competitors. Yet, we are still treating the licenses as extensions of the old, proprietary world and defending perceived injustices based on that model. + +> Side Note: Full compliance with all license and other legal rules is a given here. + +## Documenso’s Approach: Co-Ownership and Community + +So, if you want to fork Documenso and build a business on it, you can. Whether that’s a cool thing to do is another matter. Whether you do a better job than us is also another matter (you won’t). But if you do, I’ll be the first to join. But why not join us from the start since you already have the upside? We exist because we believe this to be the best way forward—not because we force it. + +## The Bigger Picture: Open-Source as Progress + +I’ve also thought a lot about question #3. I understand the impulse to fight anyone who doesn’t appreciate this collaborative approach, but there is no part of this model that backs that up. You are free to “exploit” as long as it’s in a way that adds value. The fallacy is in considering someone else using the OSS part for their business as treason, which it’s not. It’s the whole point. + +While some might say this is theoretical and that reality is different, this is the version of Open Source on which we are building Documenso. The point here is that OSS companies must be resilient to handle forking and competition; without this resilience, an open source driven economy can’t thrive. The focus on freedom and collaboration means being prepared for forks and challenges as part of the growth, not as threats. + +Of course, all of this applies to Documenso, the OSS project, not Documenso Inc., the company, which is very much a privately owned, for-profit entity. However, since the goal is to scale Documenso to the entire world, there is plenty of room to see everyone as co-owners of the Open Source project rather than as competitors. In the end, Open Source is about progress through freedom. If you don’t like how we run things, go fork yourself and hold us accountable. We don’t own this; we just happened to start it. + +> Since this article is open source as well, you are free to fork it and change it here: [https://documen.so/repo](https://documen.so/repo) + +If you have any questions or comments, please reach out on [Twitter / X](https://twitter.com/eltimuro) (DM open) or [Discord](https://documen.so/discord). + +Thinking about switching to a modern signing platform? Reach out anytime: [https://documen.so/sales](https://documen.so/sales) + +Best from Hamburg\ +Timur diff --git a/apps/marketing/content/blog/introducing-embedding.mdx b/apps/marketing/content/blog/introducing-embedding.mdx new file mode 100644 index 000000000..45a04aec2 --- /dev/null +++ b/apps/marketing/content/blog/introducing-embedding.mdx @@ -0,0 +1,72 @@ +--- +title: 'Introducing Embedding Support for Documenso' +description: 'Embedding is now here! Learn how we built it and how it can be used to bring e-signing to your own applications.' +authorName: 'Lucas Smith' +authorImage: '/blog/blog-author-lucas.png' +authorRole: 'Co-Founder' +date: 2024-09-06 +tags: + - Development +--- + +When we first launched Documenso, one of the most requested features was embedding. We knew it was important and aligned with our desire to not just be a e-signing application but to instead provide the e-signature infrastructure for the web and beyond. + +With that said, we decided to hold off initially so we could focus on building a solid, well-featured core application. Looking back, this was definitely the right call. Embedding is only as good as the features behind it, and we didn't want to release something that wasn't ready to meet user and developer expectations. + +Over the past year, we've been busy adding tons of new features and reaching new levels of compliance, like 21 CFR Part 11. We've also introduced [new fields](/blog/introducing-advanced-signing-fields), [built out an API](/blog/public-api), [added webhooks, integrations with Zapier](/blog/launch-week-2-day-4), and a lot more. + +Now that we've laid a solid foundation, it's finally time to focus on embedding, the top-requested feature from both our users and those self-hosting our platform. + +## Why Embedding Took Time + +In previous projects, I’ve often seen embedding built by bundling components for use in a client’s website or app. This method gives users maximum flexibility for styling and behavior, while avoiding certain cross-origin issues. However, it can also introduce problems like code conflicts or performance bottlenecks. For example, third-party tools such as Google Tag Manager (GTM) or other marketing scripts can interfere with your SDK. Additionally, the SDK must remain lightweight to avoid slowing down the client’s page. + +For Documenso, we decided to explore a different approach. After carefully researching our options, we opted for an iframe-based solution. While iframes are typically less flexible—especially when it comes to theming or passing pre-filled data containing personally identifiable information (PII)—we identified ways to mitigate these concerns. + +One of the biggest challenges was ensuring that we could pass sensitive data, like emails for pre-filling forms, without exposing PII to our server. To solve this, we used [fragment identifiers](https://developer.mozilla.org/en-US/docs/Web/API/URL/hash) in the URL, which are processed client-side and never sent in network requests. This method ensures that PII is protected and not logged by our server or any intermediate web services. + +### Using the PostMessage API for Communication + +To maintain a high level of interactivity, our iframes communicate with the parent window using the [postMessage API](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage). This allows us to notify the parent app when specific events occur inside the iframe, creating a more dynamic user experience and bridging the gap between our iframe-based solution and typical fat SDKs. + +Additionally, props are passed into the iframe via the [fragment identifier](https://developer.mozilla.org/en-US/docs/Web/API/URL/hash) of the URL. This avoids the need for complex two-way data synchronization between the parent and child frames, making the system stable and more reliable. + +### Building the Embeds with Mitosis + +Given that our iframe solution is quite lightweight, we saw this as a great opportunity to experiment with [Mitosis](https://mitosis.builder.io/) which would let us do something truly special. For those unfamiliar, Mitosis is a project by Builder.io that lets you write components once and then transpile them into a variety of frameworks like React, Vue, and Svelte. + +We used Mitosis to build two key components: a direct template embed and a document signing embed. The direct template allows users to use a template as if it were an evergreen document—meaning that, when someone completes the template, a new document is automatically generated. This is the use case we expect most users to adopt for embedding. For more advanced workflows, we also offer a document signing embed, which can handle multi-recipient workflows and other complex scenarios intended for use in deeper, rich integrations. + +Mitosis allowed us to quickly target several popular frameworks, including [React](https://www.npmjs.com/package/@documenso/embed-react), [Preact](https://www.npmjs.com/package/@documenso/embed-preact), [Vue](https://www.npmjs.com/package/@documenso/embed-vue), [Svelte](https://www.npmjs.com/package/@documenso/embed-svelte), and [SolidJS](https://www.npmjs.com/package/@documenso/embed-solid). + +I had also hoped to include Angular, but while Mitosis makes it really easy to transpile component, we still have to take care of bundling and packaging the resulting component ourselves. While the above frameworks can all be bundled using Vite.js, Angular still has it's own set of tooling that we would need to learn and use. Given this constraint we opted to put Angular on hold for now while we wait for the newer Vite.js support to mature. + +### Challenges and Lessons with Mitosis and more + +While our experience with Mitosis was largely positive, there were some challenges along the way. For instance, certain state properties with the same names as props caused issues during the transpilation process, leading to type errors and unexpected transpilation results with some targets. + +This was also a challenge since our initial implementation of the two components had some minor separation of concerns which also resulted in some transpilation issues with some targets. We addressed this by removing the separation of concerns for now since it was mostly for show rather than out of necessity. + +On top of that, packaging and publishing the embeds posed its own set of challenges, particularly given the growing complexity of JavaScript package management. Tools like [Publint](https://www.npmjs.com/package/publint) helped streamline the process by ensuring we followed best practices for both CommonJS and ESM formats. + +### To the Future, The Documenso Platform + +With the embedding feature now in place, we're excited to continue expanding Documenso's capabilities. Embeds are just the beginning of what we're calling the Documenso platform. Through our user research, we've learned that while many businesses appreciate having a flexible e-signature solution, they're even more interested in using our tools to build signing functionality directly into their own apps—without worrying about the technical complexities of compliance and security that come with e-signing. + +Over the coming months, we'll be working on enhancing our API, strengthening integrations with tools like Zapier, and improving our webhook system. Our goal is to give users the ability to embed e-signatures and document management wherever they need it, whether that's through self-hosting or by using Documenso as a platform. We can't wait to see how our users and self-hosters leverage these new capabilities! + +### Ready to Get Started? + +If you're ready to embed document signing into your own app or website, check out our [Embedding Documentation](https://docs.documenso.com/developers/embedding?utm_source=blog&utm_campaign=introducing-embedding) to see how easy it is to get started. You'll find everything you need to get started today! + +