API

How To Create and Send Envelopes with Documenso

How To Create and Send Envelopes with Documenso

Nov 13, 2025

How to create and send envelopes with Documenso

As part of the Documenso 2.0 rollout, we’ve introduced a new Envelopes API. Envelopes are the natural progression of documents and templates, designed to simplify multi-document signing workflows.

Envelopes allow you to:

  • Use multiple PDF files in a single workflow

  • Avoid having to merge everything into one large PDF

  • Work with either document or template envelope types

The envelopes API is available under our v2 API which has now exited beta with the release of Documenso 2.0!

In this article, we'll explore how you can create and configure envelopes via the API. We'll go over uploading multiple files, adding recipients, fields and configuring the meta information.

Creating an envelope

You create an envelope by making a POST request to the /api/v2/envelope/create endpoint with a multipart/form-data payload. The payload must contain the following fields at the minimum:

  • payload.title - The name you want to use for the envelope.

  • payload.type - The type of the envelope you want to create. You can choose either DOCUMENT or TEMPLATE depending on the type of documents you want to send.

The request should also contain the files you want to send in the envelope. You can add multiple files to the request.

cURL example:

curl -X POST "https://app.documenso.com/api/v2/envelope/create" \
  -H "Authorization: <YOUR_TOKEN>" \
  -H "Content-Type: multipart/form-data" \
  -F 'payload={
    "type": "DOCUMENT",
    "title": "Envelope Full Field Test",
  }' \
  -F "files=@./your-first-pdf.pdf;type=application/pdf" \
  -F "files=@./your-second-pdf.pdf;type=application/pdf"

Node.js Fetch example:

import fs from "fs";
import path from "path";
const __dirname = path.resolve();

const endpoint = "https://app.documenso.com/api/v2/envelope/create";
const token = "<YOUR_TOKEN>";

const firstPDF = fs.readFileSync(path.join(__dirname, "./your-first-pdf.pdf"));
const secondPDF = fs.readFileSync(
  path.join(__dirname, "./your-second-pdf.pdf")
);

const formData = new FormData();

formData.append(
  "payload",
  JSON.stringify({
    type: "DOCUMENT",
    title: "Envelope Full Field Test",
  })
);

formData.append(
  "files",
  new File([firstPDF], "first-pdf.pdf", { type: "application/pdf" })
);

formData.append(
  "files",
  new File([secondPDF], "second-pdf.pdf", { type: "application/pdf" })
);

const response = await fetch(endpoint, {
  method: "POST",
  body: formData,
  headers: {
    Authorization: `Bearer ${token}`,
  },
});

Once you run either code, you should receive a response with the ID of the envelope and should be able to see the envelope in the UI.

The payload can also contain the following fields:

  • payload.externalId - A custom external ID you can use to identify the envelope.

  • payload.visibility - The visibility of the envelope. You can choose either EVERYONE, MANAGER_AND_ABOVE or ADMIN depending on who should be able to access the envelope.

  • payload.globalAccessAuth - The authentication level required to access the envelope. The available options are ACCOUNT and TWO_FACTOR_AUTH.

  • payload.globalActionAuth - The authentication level required to perform actions on the envelope. The available options are ACCOUNT, PASSKEY, TWO_FACTOR_AUTH and PASSWORD.

  • payload.formValues - The form values of the envelope. You can use this to prefill the fields of the envelope.

  • payload.folderId - The ID of the folder you want to create the envelope in. If not provided, the envelope will be created in the root folder.

  • payload.recipients - The envelope recipients.

  • payload.meta - You can use this field to specify extra information about the envelope, such as the email subject and message to use for emails, timezone, date format, distribution method, allow dictate next signer, language, typed signature, upload signature, draw signature, and email settings.

  • payload.attachments - The envelope attachments. You can use this field to provide extra context to the envelope.

These properties are optional, but they allow you to configure the envelope in more detail to fit your needs. Sometimes, you may simply want to create an empty envelope. Other times, you may want to require 2FA to access the envelope, or you may want to restrict its visibility to managers and admins, or you may want to add attachments, to name a few.

Adding recipients

There are cases where you might want to create envelopes without recipients. But in most cases, you will want to add the recipients as well in the envelope creation process. You can add multiple recipients to an evenlope by providing the recipients field in the payload.

The recipients field is an array of objects with the following properties:

  • email (required)

  • name (required)

  • role (required) - The available options are SIGNER, APPROVER, CC, VIEWER, APPROVER, AND ASSISTANT.

  • signingOrder (optional) - The order in which the recipient will sign the envelope. If not provided, the recipient will be added in the order of the array.

cURL example:

curl -X POST "https://app.documenso.com/api/v2/envelope/create" \
  -H "Authorization: <YOUR_TOKEN>" \
  -H "Content-Type: multipart/form-data" \
  -F 'payload={
    "type": "DOCUMENT",
    "title": "Envelope Full Field Test",
    "recipients": [
      {
        "email": "signer-first@documenso.com",
        "name": "Signer First",
        "role": "SIGNER",
        "signingOrder": 2
      },
      {
        "email": "signer-second@documenso.com",
        "name": "Signer Second",
        "role": "APPROVER",
        "signingOrder": 1
      }
    ]
  }' \
  -F "files=@./your-first-pdf.pdf;type=application/pdf" \
  -F "files=@./your-second-pdf.pdf;type=application/pdf"

Node.js Fetch example:

import fs from "fs";
import path from "path";
const __dirname = path.resolve();

const endpoint = "https://app.documenso.com/api/v2/envelope/create";
const token = "<YOUR_TOKEN>";

const firstPDF = fs.readFileSync(path.join(__dirname, "./your-first-pdf.pdf"));
const secondPDF = fs.readFileSync(
  path.join(__dirname, "./your-second-pdf.pdf")
);

const formData = new FormData();

formData.append(
  "payload",
  JSON.stringify({
    type: "DOCUMENT",
    title: "Envelope Full Field Test",
    recipients: [
      {
        email: "signer-first@documenso.com",
        name: "Signer First",
        role: "SIGNER",
        signingOrder: 2,
      },
      {
        email: "signer-second@documenso.com",
        name: "Signer Second",
        role: "APPROVER",
        signingOrder: 1,
      },
    ],
  })
);

formData.append(
  "files",
  new File([firstPDF], "first-pdf.pdf", { type: "application/pdf" })
);

formData.append(
  "files",
  new File([secondPDF], "second-pdf.pdf", { type: "application/pdf" })
);

const response = await fetch(endpoint, {
  method: "POST",
  body: formData,
  headers: {
    Authorization: `Bearer ${token}`,
  },
});

Adding fields

You can add one or more fields to a recipient by providing the fields field in the payload, for each recipient you want to add fields to. The fields property is an array of objects with the following properties:

  • type (required) - The type of the field you want to add. The available options are: SIGNATURE, FREE_SIGNATURE, INITIALS, NAME, EMAIL, DATE, TEXT, NUMBER, RADIO, CHECKBOX, DROPDOWN.

  • identifier (optional) - Either the filename (string) or the index (number, 0-based) of the envelope item/document that the field should be attached to. If not provided, the field will be attached to the first envelope item. For example, if you have 2 files in the envelope, and you want to attach the field to the second file, you can use identifier: 1.

  • page (required) - The page number of the field you want to add.

  • positionX (required) - The X position of the field you want to add (0-100).

  • positionY (required) - The Y position of the field you want to add (0-100).

  • width (required) - The width of the field you want to add (0-100).

  • height (required) - The height of the field you want to add (0-100).

  • fieldMeta (optional) - An object containing field-specific metadata such as label, placeholder, required, readOnly, fontSize, textAlign, type, and other type-specific properties. Each field type has its own default fieldMeta structure.

cURL example:

curl -X POST "https://app.documenso.com/api/v2/envelope/create" \
  -H "Authorization: <YOUR_TOKEN>" \
  -F 'payload={
    "type": "DOCUMENT",
    "title": "Envelope Full Field Test",
    "recipients": [
      {
        "email": "signer-first@documenso.com",
        "name": "Signer First",
        "role": "SIGNER",
        "signingOrder": 2,
        "fields": [
          {
            "type": "SIGNATURE",
            "identifier": 1,
            "page": 1,
            "positionX": 20,
            "positionY": 20,
            "width": 49,
            "height": 4.9
          },
          {
            "type": "SIGNATURE",
            "identifier": 0,
            "page": 1,
            "positionX": 10,
            "positionY": 50,
            "width": 49,
            "height": 4.9
          }
        ]
      },
      {
        "email": "signer-second@documenso.com",
        "name": "Signer Second",
        "role": "APPROVER",
        "signingOrder": 1,
        "fields": [
          {
            "type": "SIGNATURE",
            "page": 1,
            "positionX": 50,
            "positionY": 20,
            "width": 25,
            "height": 2.5
          },
          {
            "type": "TEXT",
            "page": 1,
            "positionX": 10,
            "positionY": 35,
            "width": 25,
            "height": 2.5,
            "fieldMeta": {
              "label": "Address",
              "placeholder": "32 New York Street, 41241",
              "text": "32 New York Street, 41241",
              "required": true,
              "readOnly": false,
              "fontSize": 90,
              "textAlign": "left",
              "type": "text"
            }
          }
        ]
      }
    ]
  }' \
  -F "files=@./your-first-pdf.pdf;type=application/pdf" \
  -F "files=@./your-second-pdf.pdf;type=application/pdf"

Node.js Fetch example:

import fs from "fs";
import path from "path";
const __dirname = path.resolve();

const endpoint = "https://app.documenso.com/api/v2/envelope/create";
const token = "<YOUR_TOKEN>";

const firstPDF = fs.readFileSync(path.join(__dirname, "./your-first-pdf.pdf"));
const secondPDF = fs.readFileSync(
  path.join(__dirname, "./your-second-pdf.pdf")
);

const formData = new FormData();

formData.append(
  "payload",
  JSON.stringify({
    type: "DOCUMENT",
    title: "Envelope Full Field Test",
    recipients: [
      {
        email: "signer-first@documenso.com",
        name: "Signer First",
        role: "SIGNER",
        signingOrder: 2,
        fields: [
          {
            type: "SIGNATURE",
            identifier: 1,
            page: 1,
            positionX: 20,
            positionY: 20,
            width: 49,
            height: 4.9,
          },
          {
            type: "SIGNATURE",
            identifier: 0,
            page: 1,
            positionX: 10,
            positionY: 50,
            width: 49,
            height: 4.9,
          },
        ],
      },
      {
        email: "signer-second@documenso.com",
        name: "Signer Second",
        role: "APPROVER",
        signingOrder: 1,
        fields: [
          {
            type: "SIGNATURE",
            page: 1,
            positionX: 50,
            positionY: 20,
            width: 25,
            height: 2.5,
          },
          {
            type: "TEXT",
            page: 1,
            positionX: 10,
            positionY: 35,
            width: 25,
            height: 2.5,
            fieldMeta: {
              label: "Address",
              placeholder: "32 New York Street, 41241",
              text: "32 New York Street, 41241",
              required: true,
              readOnly: false,
              fontSize: 90,
              textAlign: "left",
              type: "text",
            },
          },
        ],
      },
    ],
  })
);

formData.append(
  "files",
  new File([firstPDF], "first-pdf.pdf", { type: "application/pdf" })
);

formData.append(
  "files",
  new File([secondPDF], "second-pdf.pdf", { type: "application/pdf" })
);

const response = await fetch(endpoint, {
  method: "POST",
  body: formData,
  headers: {
    Authorization: `Bearer ${token}`,
  },
});

The above examples will add two fields to the first recipient and two fields to the second recipient.

Adding meta information

Lastly, you can further configure the envelope by providing the meta field in the payload. The meta property is an object with the following properties:

  • 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 of the envelope.

  • dateFormat - The date format used to display the dates in the envelope.

  • distributionMethod - The distribution method of the envelope. You can pick EMAIL to send emails to the recipients, or NONE to not send any emails.

  • signingOrder - The signing order of the envelope. You can choose between PARALLEL and SEQUENTIAL.

  • allowDictateNextSigner - Whether to allow the next signer to dictate the next signer.

  • redirectUrl - The URL where the recipients will be redirected after signing the envelope.

  • language - The language of the envelope.

  • typedSignatureEnabled - Whether to allow the signer to use a typed signature.

  • uploadSignatureEnabled - Whether to allow the signer to upload a signature.

  • drawSignatureEnabled - Whether to allow the signer to draw a signature.

  • emailId - The email ID.

  • emailReplyTo - The email the recipients will reply to.

  • emailSettings - This field allows you to customize the email notifications that will be sent to the recipients:

    • recipientSigningRequest - Whether to send an email with a signing request to the recipients.

    • recipientRemoved - Whether to send an email when a recipient is removed from the envelope.

    • recipientSigned - Whether to send an email when a recipient signs the envelope.

    • documentPending - Whether to send an email when a document is pending.

    • documentCompleted - Whether to send an email when a document is completed.

    • documentDeleted - Whether to send an email when a document is deleted.

    • ownerDocumentCompleted - Whether to send an email when the owner's document is completed.

cURL example:

curl -X POST "https://app.documenso.com/api/v2/envelope/create" \
  -H "Authorization: <YOUR_TOKEN>" \
  -F 'payload={
    "type": "DOCUMENT",
    "title": "Envelope Meta Test",
    "meta": {
      "subject": "Envelope Full Field Test",
      "message": "Envelope Full Field Test",
      "timezone": "Etc/UTC",
      "dateFormat": "yyyy-MM-dd hh:mm a",
      "distributionMethod": "EMAIL",
      "signingOrder": "PARALLEL",
      "allowDictateNextSigner": false,
      "redirectUrl": "https://www.documenso.com",
      "language": "en",
      "typedSignatureEnabled": true,
      "uploadSignatureEnabled": false,
      "drawSignatureEnabled": false,
      "emailReplyTo": "info@documenso.com",
      "emailSettings": {
        "recipientSigningRequest": true,
        "recipientRemoved": true,
        "recipientSigned": true,
        "documentPending": false,
        "documentCompleted": false,
        "documentDeleted": true,
        "ownerDocumentCompleted": true
      }
    }
  }' \
  -F "files=@./your-first-pdf.pdf;type=application/pdf" \
  -F "files=@./your-second-pdf.pdf;type=application/pdf"

Node.js Fetch example:

import fs from "fs";
import path from "path";
const __dirname = path.resolve();

const endpoint = "https://app.documenso.com/api/v2/envelope/create";
const token = "<YOUR_TOKEN>";

const firstPDF = fs.readFileSync(
  path.join(__dirname, "./your-first-pdf.pdf")

const secondPDF = fs.readFileSync(
  path.join(__dirname, "./your-second-pdf.pdf")
);

const formData = new FormData();

formData.append(
  "payload",
  JSON.stringify({
    type: "DOCUMENT",
    title: "Envelope Meta Test",
    meta: {
      subject: "Envelope Full Field Test",
      message: "Envelope Full Field Test",
      timezone: "Etc/UTC",
      dateFormat: "yyyy-MM-dd hh:mm a",
      distributionMethod: "EMAIL",
      signingOrder: "PARALLEL",
      allowDictateNextSigner: false,
      redirectUrl: "https://www.documenso.com",
      language: "en",
      typedSignatureEnabled: true,
      uploadSignatureEnabled: false,
      drawSignatureEnabled: false,
      emailReplyTo: "info@documenso.com",
      emailSettings: {
        recipientSigningRequest: true,
        recipientRemoved: true,
        recipientSigned: true,
        documentPending: false,
        documentCompleted: false,
        documentDeleted: true,
        ownerDocumentCompleted: true,
      },
    },
  })
);

formData.append(
  "files",
  new File([firstPDF], "first-pdf.pdf", { type: "application/pdf" })
);

formData.append(
  "files",
  new File([secondPDF], "second-pdf.pdf", { type: "application/pdf" })
);

const response = await fetch(endpoint, {
  method: "POST",
  body: formData,
  headers: {
    Authorization: `Bearer ${token}`,
  },
});

If you now visit the envelope in the UI, you should see the envelope with the settings you configured.

Sending the envelope

Once you created and configured the envelope to your liking, you can send it to the recipients using the api/v2/envelope/distribute endpoint.

You'll need the envelope ID for this request, which you can find in the response of the api/v2/envelope/create endpoint; the one we used in the previous examples. You can also configure the envelope through the meta field in the payload, in case you didn't configure it during the envelope creation.

cURL example:

curl "https://app.documenso.com/api/v2/envelope/distribute" \
  -X POST \
  -H "Authorization: <YOUR_TOKEN>" \
  -H "Content-Type: application/json" \
  --data '{
  "envelopeId": "envelope_meswwzicykehufle"
}'

Node.js Fetch example:

import fs from "fs";
import path from "path";
const __dirname = path.resolve();

const endpoint = "https://app.documenso.com/api/v2/envelope/create";
const token = "<YOUR_TOKEN>";

const firstPDF = fs.readFileSync(path.join(__dirname, "./your-first-pdf.pdf"));
const secondPDF = fs.readFileSync(
  path.join(__dirname, "./your-second-pdf.pdf")
);

const formData = new FormData();

formData.append(
  "payload",
  JSON.stringify({
    type: "DOCUMENT",
    title: "Envelope Full Field Test",
    recipients: [
      {
        email: "signer-first@documenso.com",
        name: "Signer First",
        role: "SIGNER",
        signingOrder: 2,
        fields: [
          {
            type: "SIGNATURE",
            identifier: 1,
            page: 1,
            positionX: 20,
            positionY: 20,
            width: 49,
            height: 4.9,
          },
          {
            type: "SIGNATURE",
            identifier: 0,
            page: 1,
            positionX: 10,
            positionY: 50,
            width: 49,
            height: 4.9,
          },
        ],
      },
      {
        email: "signer-second@documenso.com",
        name: "Signer Second",
        role: "APPROVER",
        signingOrder: 1,
        fields: [
          {
            type: "SIGNATURE",
            page: 1,
            positionX: 50,
            positionY: 20,
            width: 25,
            height: 2.5,
          },
          {
            type: "TEXT",
            page: 1,
            positionX: 10,
            positionY: 35,
            width: 25,
            height: 2.5,
            fieldMeta: {
              label: "Address",
              placeholder: "32 New York Street, 41241",
              text: "32 New York Street, 41241",
              required: true,
              readOnly: false,
              fontSize: 90,
              textAlign: "left",
              type: "text",
            },
          },
        ],
      },
    ],
  })
);

formData.append(
  "files",
  new File([firstPDF], "first-pdf.pdf", { type: "application/pdf" })
);

formData.append(
  "files",
  new File([secondPDF], "second-pdf.pdf", { type: "application/pdf" })
);

const response = await fetch(endpoint, {
  method: "POST",
  body: formData,
  headers: {
    Authorization: `Bearer ${token}`,
  },
});

const { id: envelopeId } = await response.json();

const distributeEndpoint =
  "https://app.documenso.com/api/v2/envelope/distribute";

const distributeResponse = await fetch(distributeEndpoint, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${token}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ envelopeId }),
});

For more details and the full list of parameters, see the Distribute Envelope endpoint documentation.

Completed envelopes retrieval

Once the envelope is completed, you can download its documents individually with a GET request to the api/v2/envelope/item/{envelopeItemId}/download endpoint.

However, you'll need the envelope item ID for each document you want to download. You can retrieve all the files associated with an envelope via a GET request to the api/v2/envelope/{envelopeId}> endpoint, which includes their IDs.

curl "https://app.documenso.com/api/v2/envelope/{envelopeId}" \
  -H "Authorization: <YOUR_TOKEN>"

Then, you can download specific files by passing their ID to the api/v2/envelope/item/{envelopeItemId}/download endpoint.

curl "https://app.documenso.com/api/v2/envelope/item/{envelopeItemId}/download" \
  -H "Authorization: <YOUR_TOKEN>"

Documenso SDKs

But it gets even better. Documenso provides SDKs for multiple programming languages, so you can seamlessly integrate Documenso into your applications and workflows. The available SDKs are:

All SDKs are fully typed and make it quick and easy to get started with Documenso.

Want to learn more?

To go deeper on what’s possible with envelopes:

That’s all you need to start building with the Documenso Envelopes API.

Documenso

© 2025 Documenso, Inc. All rights reserved.

Documenso

© 2025 Documenso, Inc. All rights reserved.

Documenso

© 2025 Documenso, Inc. All rights reserved.