> You are reading the Docusign Developer Documentation. For clean Markdown of any page, append .md to the page URL.
>
> Documentation indexes by section:
> - APIs and SDKs: https://docusign.docs.buildwithfern.com/docs/llms.txt
> - Platform 101: https://docusign.docs.buildwithfern.com/platform/llms.txt
> - Extension Apps: https://docusign.docs.buildwithfern.com/extension-apps/llms.txt
> - Docusign CLI: https://docusign.docs.buildwithfern.com/docusign-cli/llms.txt
>
> Authentication (OAuth, JWT Grant, M2M): https://docusign.docs.buildwithfern.com/platform/auth
> Webhooks and Connect: https://docusign.docs.buildwithfern.com/platform/webhooks
>
> OpenAPI specifications: https://docusign.docs.buildwithfern.com/openapi.json
> MCP server: https://docusign.docs.buildwithfern.com/_mcp/server

# Quickstart

> Authenticate with Docusign and send your first envelope for remote signing using Bash, C#, Java, Node.js, PHP, PowerShell, Python, or Ruby.

> **Note**
>
> Welcome to the new Quickstart guide! Looking for the original Quickstart repo? Check out the [Docusign code example launchers](/tools/code-example-launchers) for setup instructions.

## Make your first API call with Docusign

Authenticate with Docusign, create your first envelope, and send an agreement for remote signing via email in a single API call.

In this quickstart, you will obtain an [OAuth access token](https://developers.docusign.com/platform/auth/authcode/), create an [envelope](https://developers.docusign.com/docs/esign-rest-api/esign101/concepts/envelopes/) containing a document and a signature field, and send it to your recipient's email inbox. Choose Bash, C#, Java, Node.js, PHP, PowerShell, Python, or Ruby. Code examples for some languages use [Docusign SDKs](https://developers.docusign.com/docs/sdks/). The workflow uses OAuth 2.0 JWT Grant (Service Integration) in the developer environment.

An [envelope](https://developers.docusign.com/docs/esign-rest-api/esign101/concepts/envelopes/) contains the [documents](https://developers.docusign.com/docs/esign-rest-api/esign101/concepts/documents/), [recipients](https://developers.docusign.com/docs/esign-rest-api/esign101/concepts/recipients/), and fields for a signature transaction.

## Prerequisites

### Create a developer account

Create a free Docusign developer account at [https://www.docusign.com/developers/sandbox](https://www.docusign.com/developers/sandbox). Use this account to build and test in the developer environment.

### Create and configure an integration key

An integration key, also called a client ID, identifies your application.

1. Sign in to your developer account and open Apps and Keys.
2. Select Add App and Integration Key, name your app, and create it.
3. Copy the integration key.
4. Under User Application, select + Add RSA Keypair. Download and save the private key as `private.key`.
5. Under Additional settings, add a Redirect URI: `https://developers.docusign.com` and save your changes.

**Grant one-time consent:**

Open this URL in your browser once to authorize your integration key (replace `{INTEGRATION_KEY}` with your key):

```text
https://account-d.docusign.com/oauth/auth?response_type=code&scope=signature%20impersonation&client_id={INTEGRATION_KEY}&redirect_uri=https://developers.docusign.com
```

### Obtain your OAuth access token

To make the API call shown in this Quickstart, you need a valid OAuth access token. Docusign recommends three OAuth grant types: Public Authorization Code Grant, Confidential Authorization Code Grant, and JSON Web Token (JWT) Grant. Choose the grant type appropriate for your application and follow the [authentication instructions](https://developers.docusign.com/platform/auth/) to obtain a token. The SDK examples in this Quickstart use JWT Grant.

### Install your language dependencies

Find your language below and complete its setup.

#### Bash

Use Bash with cURL 7.76 or later, jq, and base64. This example calls the REST API directly; no Docusign SDK is required. Save the authentication and API blocks in `quickstart.sh`.

#### C\#

Create a .NET console project. Add the Docusign SDK, then put the authentication and API blocks in `Program.cs`.

```bash
dotnet new console --name DocusignQuickstart
cd DocusignQuickstart
dotnet add package DocuSign.eSign.dll
```

#### Java

Create a Maven project and add this dependency to `pom.xml`. Put the authentication and API blocks inside the main method of `Quickstart.java`; keep the imports above the class declaration.

```xml
<dependency>
    <groupId>com.docusign</groupId>
    <artifactId>docusign-esign-java</artifactId>
    <version>6.9.0</version>
</dependency>
```

#### Node.js

Install Node.js and the Docusign SDK. Save the authentication and API blocks in `quickstart.mjs`.

```bash
npm init -y
npm install docusign-esign
```

#### PHP

Install PHP with cURL, JSON, and MBString support, plus Composer. Save the authentication and API blocks in `quickstart.php`.

```bash
composer require docusign/esign-client
```

#### PowerShell

Use PowerShell. This example calls the REST API directly; no Docusign SDK is required. Save the authentication and API blocks in `quickstart.ps1`.

#### Python

Install Python and the Docusign SDK. Save the authentication and API blocks in `quickstart.py`.

```bash
python -m pip install docusign_esign
```

#### Ruby

Install Ruby and the Docusign SDK. Save the authentication and API blocks in `quickstart.rb`.

```bash
gem install docusign_esign
```

## Call the Docusign API: Send an envelope via email

Remote signing is Docusign's core workflow: your application creates an envelope containing your document and signature fields, and Docusign delivers an email invitation to the recipient and guides them through signing.

Create and send the envelope in a single API call:

```http
POST /v2.1/accounts/{accountId}/envelopes
```

Set `status` to `sent` in the request body to send the signing invitation. Select a language tab below to view its code example.

#### Bash

```bash
curl -s -X POST \
  "https://demo.docusign.net/restapi/v2.1/accounts/{accountId}/envelopes" \
  -H "Authorization: Bearer <ACCESS_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "emailSubject": "Please sign this document from Docusign Quickstart",
    "status": "sent",
    "documents": [
      {
        "documentBase64": "<BASE64_DOCUMENT_BYTES>",
        "name": "Welcome Agreement",
        "fileExtension": "txt",
        "documentId": "1"
      }
    ],
    "recipients": {
      "signers": [
        {
          "email": "alex.developer@example.com",
          "name": "Alex Developer",
          "recipientId": "1",
          "routingOrder": "1",
          "tabs": {
            "signHereTabs": [
              {
                "documentId": "1",
                "pageNumber": "1",
                "recipientId": "1",
                "xPosition": "150",
                "yPosition": "150"
              }
            ]
          }
        }
      ]
    }
  }'
```

#### C\#

```csharp
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using DocuSign.eSign.Api;
using DocuSign.eSign.Client;
using DocuSign.eSign.Model;

class Program
{
    static void Main()
    {
        var basePath = "https://demo.docusign.net/restapi";
        var oauthHost = "account-d.docusign.com";

        var clientId = Environment.GetEnvironmentVariable("DOCUSIGN_CLIENT_ID");
        var userId = Environment.GetEnvironmentVariable("DOCUSIGN_USER_ID");
        var accountId = Environment.GetEnvironmentVariable("DOCUSIGN_ACCOUNT_ID");
        var keyPath = Environment.GetEnvironmentVariable("DOCUSIGN_KEY_FILE")
            ?? "private.key";
        var signerEmail = Environment.GetEnvironmentVariable("SIGNER_EMAIL")
            ?? "alex.developer@example.com";
        var signerName = Environment.GetEnvironmentVariable("SIGNER_NAME")
            ?? "Alex Developer";

        // 1. Authenticate using JWT Grant.
        var privateKeyBytes = File.ReadAllBytes(keyPath);
        var apiClient = new DocuSignClient(basePath);

        var token = apiClient.RequestJWTUserToken(
            clientId,
            userId,
            oauthHost,
            privateKeyBytes,
            1,
            new List<string> { "signature", "impersonation" }
        );

        apiClient.Configuration.DefaultHeader["Authorization"] =
            "Bearer " + token.access_token;

        var envelopesApi = new EnvelopesApi(apiClient);

        // 2. Prepare the document and signature field.
        var docContent = "Welcome to Docusign! Please sign this agreement.";
        var docBase64 = Convert.ToBase64String(
            Encoding.UTF8.GetBytes(docContent)
        );

        var document = new Document
        {
            DocumentBase64 = docBase64,
            Name = "Welcome Agreement",
            FileExtension = "txt",
            DocumentId = "1"
        };

        var signHere = new SignHere
        {
            DocumentId = "1",
            PageNumber = "1",
            RecipientId = "1",
            XPosition = "150",
            YPosition = "150"
        };

        // Omitting ClientUserId enables remote signing via email.
        var signer = new Signer
        {
            Email = signerEmail,
            Name = signerName,
            RecipientId = "1",
            RoutingOrder = "1",
            Tabs = new Tabs
            {
                SignHereTabs = new List<SignHere> { signHere }
            }
        };

        var envelopeDefinition = new EnvelopeDefinition
        {
            EmailSubject = "Please sign this document from Docusign Quickstart",
            Documents = new List<Document> { document },
            Recipients = new Recipients
            {
                Signers = new List<Signer> { signer }
            },
            Status = "sent"
        };

        // 3. Create and send the envelope in one API call.
        var summary = envelopesApi.CreateEnvelope(
            accountId,
            envelopeDefinition
        );

        Console.WriteLine(
            $"Success! Envelope sent. Envelope ID: {summary.EnvelopeId}"
        );
        Console.WriteLine(
            $"An email invitation has been sent to {signerEmail}."
        );
    }
}

```

#### Java

```java
import com.docusign.esign.api.EnvelopesApi;
import com.docusign.esign.client.ApiClient;
import com.docusign.esign.client.auth.OAuth;
import com.docusign.esign.model.*;

import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.List;

public class Quickstart {
    public static void main(String[] args) throws Exception {
        String basePath = "https://demo.docusign.net/restapi";
        String oauthHost = "account-d.docusign.com";

        String clientId = System.getenv("DOCUSIGN_CLIENT_ID");
        String userId = System.getenv("DOCUSIGN_USER_ID");
        String accountId = System.getenv("DOCUSIGN_ACCOUNT_ID");

        String keyPath = System.getenv("DOCUSIGN_KEY_FILE") != null
            ? System.getenv("DOCUSIGN_KEY_FILE")
            : "private.key";

        String signerEmail = System.getenv("SIGNER_EMAIL") != null
            ? System.getenv("SIGNER_EMAIL")
            : "alex.developer@example.com";

        String signerName = System.getenv("SIGNER_NAME") != null
            ? System.getenv("SIGNER_NAME")
            : "Alex Developer";

        // 1. Authenticate using JWT Grant.
        byte[] privateKeyBytes = Files.readAllBytes(Paths.get(keyPath));

        ApiClient apiClient = new ApiClient(basePath);
        apiClient.setOAuthBasePath(oauthHost);

        OAuth.OAuthToken token = apiClient.requestJWTUserToken(
            clientId,
            userId,
            List.of("signature", "impersonation"),
            privateKeyBytes,
            3600
        );

        apiClient.addDefaultHeader(
            "Authorization",
            "Bearer " + token.getAccessToken()
        );

        EnvelopesApi envelopesApi = new EnvelopesApi(apiClient);

        // 2. Prepare the document and signature field.
        String docContent = "Welcome to Docusign! Please sign this agreement.";
        String docBase64 = Base64.getEncoder().encodeToString(
            docContent.getBytes(StandardCharsets.UTF_8)
        );

        Document document = new Document();
        document.setDocumentBase64(docBase64);
        document.setName("Welcome Agreement");
        document.setFileExtension("txt");
        document.setDocumentId("1");

        SignHere signHere = new SignHere();
        signHere.setDocumentId("1");
        signHere.setPageNumber("1");
        signHere.setRecipientId("1");
        signHere.setXPosition("150");
        signHere.setYPosition("150");

        // Omitting clientUserId enables remote signing via email.
        Signer signer = new Signer();
        signer.setEmail(signerEmail);
        signer.setName(signerName);
        signer.setRecipientId("1");
        signer.setRoutingOrder("1");

        Tabs tabs = new Tabs();
        tabs.setSignHereTabs(List.of(signHere));
        signer.setTabs(tabs);

        Recipients recipients = new Recipients();
        recipients.setSigners(List.of(signer));

        EnvelopeDefinition envelopeDefinition = new EnvelopeDefinition();
        envelopeDefinition.setEmailSubject(
            "Please sign this document from Docusign Quickstart"
        );
        envelopeDefinition.setDocuments(List.of(document));
        envelopeDefinition.setRecipients(recipients);
        envelopeDefinition.setStatus("sent");

        // 3. Create and send the envelope in one API call.
        EnvelopeSummary summary = envelopesApi.createEnvelope(
            accountId,
            envelopeDefinition
        );

        System.out.println(
            "Success! Envelope sent. Envelope ID: " + summary.getEnvelopeId()
        );
        System.out.println(
            "An email invitation has been sent to " + signerEmail + "."
        );
    }
}

```

#### Node.js

```javascript
import docusign from 'docusign-esign';
import fs from 'node:fs';

const BASE_PATH = process.env.DOCUSIGN_BASE_PATH ||
  'https://demo.docusign.net/restapi';
const OAUTH_HOST = process.env.DOCUSIGN_OAUTH_HOST ||
  'account-d.docusign.com';

const CLIENT_ID = process.env.DOCUSIGN_CLIENT_ID;
const USER_ID = process.env.DOCUSIGN_USER_ID;
const ACCOUNT_ID = process.env.DOCUSIGN_ACCOUNT_ID;
const KEY_PATH = process.env.DOCUSIGN_KEY_FILE || 'private.key';
const SIGNER_NAME = process.env.SIGNER_NAME || 'Alex Developer';
const SIGNER_EMAIL = process.env.SIGNER_EMAIL ||
  'alex.developer@example.com';

async function main() {
  const apiClient = new docusign.ApiClient();
  apiClient.setBasePath(BASE_PATH);
  apiClient.setOAuthBasePath(OAUTH_HOST);

  // 1. Authenticate using JWT Grant.
  const privateKeyBytes = fs.readFileSync(KEY_PATH);

  const token = await apiClient.requestJWTUserToken(
    CLIENT_ID,
    USER_ID,
    ['signature', 'impersonation'],
    privateKeyBytes,
    3600
  );

  apiClient.addDefaultHeader(
    'Authorization',
    `Bearer ${token.body.access_token}`
  );

  const envelopesApi = new docusign.EnvelopesApi(apiClient);

  // 2. Prepare the document and signature field.
  const docContent = 'Welcome to Docusign! Please sign this agreement.';
  const docBase64 = Buffer.from(docContent).toString('base64');

  const document = new docusign.Document();
  document.documentBase64 = docBase64;
  document.name = 'Welcome Agreement';
  document.fileExtension = 'txt';
  document.documentId = '1';

  const signHere = docusign.SignHere.constructFromObject({
    documentId: '1',
    pageNumber: '1',
    recipientId: '1',
    xPosition: '150',
    yPosition: '150'
  });

  // Omitting clientUserId enables remote signing via email.
  const signer = docusign.Signer.constructFromObject({
    email: SIGNER_EMAIL,
    name: SIGNER_NAME,
    recipientId: '1',
    routingOrder: '1',
    tabs: {
      signHereTabs: [signHere]
    }
  });

  const envelopeDefinition = new docusign.EnvelopeDefinition();
  envelopeDefinition.emailSubject =
    'Please sign this document from Docusign Quickstart';
  envelopeDefinition.documents = [document];
  envelopeDefinition.recipients = { signers: [signer] };
  envelopeDefinition.status = 'sent';

  // 3. Create and send the envelope in one API call.
  const summary = await envelopesApi.createEnvelope(ACCOUNT_ID, {
    envelopeDefinition
  });

  console.log(
    `Success! Envelope sent. Envelope ID: ${summary.envelopeId}`
  );
  console.log(
    `An email invitation has been sent to ${SIGNER_EMAIL}.`
  );
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

```

#### PHP

```php
<?php
require 'vendor/autoload.php';

use DocuSign\eSign\Client\ApiClient;
use DocuSign\eSign\Configuration;
use DocuSign\eSign\Api\EnvelopesApi;
use DocuSign\eSign\Model\EnvelopeDefinition;
use DocuSign\eSign\Model\Document;
use DocuSign\eSign\Model\Signer;
use DocuSign\eSign\Model\SignHere;
use DocuSign\eSign\Model\Tabs;
use DocuSign\eSign\Model\Recipients;

$basePath = getenv('DOCUSIGN_BASE_PATH') ?: 'https://demo.docusign.net/restapi';
$oauthHost = getenv('DOCUSIGN_OAUTH_HOST') ?: 'account-d.docusign.com';
$clientId = getenv('DOCUSIGN_CLIENT_ID');
$userId = getenv('DOCUSIGN_USER_ID');
$accountId = getenv('DOCUSIGN_ACCOUNT_ID');
$keyPath = getenv('DOCUSIGN_KEY_FILE') ?: 'private.key';
$signerEmail = getenv('SIGNER_EMAIL') ?: 'alex.developer@example.com';
$signerName = getenv('SIGNER_NAME') ?: 'Alex Developer';

$privateKeyBytes = file_get_contents($keyPath);

// 1. Authenticate using JWT Grant
$config = new Configuration();
$apiClient = new ApiClient($config);
$apiClient->getOAuth()->setOAuthBasePath($oauthHost);

$tokenResponse = $apiClient->requestJWTUserToken(
    $clientId,
    $userId,
    $privateKeyBytes,
    ['signature', 'impersonation'],
    3600
);
$accessToken = $tokenResponse[0]['access_token'];

$config->setHost($basePath);
$config->addDefaultHeader('Authorization', 'Bearer ' . $accessToken);
$envelopesApi = new EnvelopesApi($apiClient);

// 2. Create and Send Envelope (Single API Call)
$docContent = "Welcome to Docusign! Please sign this agreement.";
$document = new Document([
    'document_base64' => base64_encode($docContent),
    'name' => 'Welcome Agreement',
    'file_extension' => 'txt',
    'document_id' => '1'
]);

$signHere = new SignHere([
    'document_id' => '1',
    'page_number' => '1',
    'recipient_id' => '1',
    'x_position' => '150',
    'y_position' => '150'
]);

// Omitting client_user_id prompts Docusign to send an email invitation
$signer = new Signer([
    'email' => $signerEmail,
    'name' => $signerName,
    'recipient_id' => '1',
    'routing_order' => '1',
    'tabs' => new Tabs(['sign_here_tabs' => [$signHere]])
]);

$envelopeDefinition = new EnvelopeDefinition([
    'email_subject' => 'Please sign this document from Docusign Quickstart',
    'documents' => [$document],
    'recipients' => new Recipients(['signers' => [$signer]]),
    'status' => 'sent' // 'sent' sends the email immediately
]);

$summary = $envelopesApi->createEnvelope($accountId, $envelopeDefinition);
echo "Success! Envelope sent. Envelope ID: " . $summary->getEnvelopeId() . "\n";
echo "An email invitation has been sent to " . $signerEmail . ". Check your inbox to sign!\n";
```

#### PowerShell

```powershell
$ErrorActionPreference = 'Stop'

$basePath = if ($env:DOCUSIGN_BASE_PATH) { $env:DOCUSIGN_BASE_PATH } else { "https://demo.docusign.net/restapi" }
$accountId = $env:DOCUSIGN_ACCOUNT_ID
$accessToken = $env:DOCUSIGN_ACCESS_TOKEN
$signerEmail = if ($env:SIGNER_EMAIL) { $env:SIGNER_EMAIL } else { "alex.developer@example.com" }
$signerName = if ($env:SIGNER_NAME) { $env:SIGNER_NAME } else { "Alex Developer" }

if (-not $accountId -or -not $accessToken) {
    Write-Error "DOCUSIGN_ACCOUNT_ID and DOCUSIGN_ACCESS_TOKEN must be set."
    exit 1
}

# 1. Prepare Document & Envelope Definition
$docBytes = [System.Text.Encoding]::UTF8.GetBytes("Welcome to Docusign! Please sign this agreement.")
$docBase64 = [Convert]::ToBase64String($docBytes)

# Omitting clientUserId prompts Docusign to send an email invitation
$envelopeDefinition = @{
    emailSubject = "Please sign this document from Docusign Quickstart"
    status = "sent"
    documents = @(@{
        documentBase64 = $docBase64
        name = "Welcome Agreement"
        fileExtension = "txt"
        documentId = "1"
    })
    recipients = @{
        signers = @(@{
            email = $signerEmail
            name = $signerName
            recipientId = "1"
            routingOrder = "1"
            tabs = @{
                signHereTabs = @(@{
                    documentId = "1"
                    pageNumber = "1"
                    recipientId = "1"
                    xPosition = "150"
                    yPosition = "150"
                })
            }
        })
    }
}

# 2. Send Envelope (Single API Call)
$envelope = Invoke-RestMethod -Method Post `
    -Uri "$basePath/v2.1/accounts/$accountId/envelopes" `
    -Headers @{ Authorization = "Bearer $accessToken" } `
    -ContentType 'application/json' `
    -Body ($envelopeDefinition | ConvertTo-Json -Depth 10)

Write-Output "Success! Envelope sent. Envelope ID: $($envelope.envelopeId)"
Write-Output "An email invitation has been sent to $signerEmail. Check your inbox to sign!"

```

#### Python

```python
import base64
import os

from docusign_esign import (
    ApiClient,
    Document,
    EnvelopeDefinition,
    EnvelopesApi,
    Recipients,
    Signer,
    SignHere,
    Tabs,
)
from docusign_esign.client.configuration import Configuration
from docusign_esign.client.api_response import RESTClientObject

BASE_PATH = "https://demo.docusign.net/restapi"
OAUTH_HOST = "account-d.docusign.com"

CLIENT_ID = os.environ.get("DOCUSIGN_CLIENT_ID")
USER_ID = os.environ.get("DOCUSIGN_USER_ID")
ACCOUNT_ID = os.environ.get("DOCUSIGN_ACCOUNT_ID")
KEY_PATH = os.environ.get("DOCUSIGN_KEY_FILE", "private.key")
SIGNER_NAME = os.environ.get("SIGNER_NAME", "Alex Developer")
SIGNER_EMAIL = os.environ.get(
    "SIGNER_EMAIL",
    "alex.developer@example.com",
)


def main():
    api_client = ApiClient()
    api_client.host = BASE_PATH
    api_client.set_oauth_host_name(OAUTH_HOST)

    # Optional: use your organization's trusted CA bundle.
    ca_bundle = os.environ.get("SSL_CERT_FILE")
    if ca_bundle:
        configuration = Configuration()
        configuration.ssl_ca_cert = ca_bundle
        api_client.rest_client = RESTClientObject(
            configuration=configuration
        )

    # 1. Authenticate using JWT Grant.
    with open(KEY_PATH, "rb") as key_file:
        private_key_bytes = key_file.read()

    token = api_client.request_jwt_user_token(
        client_id=CLIENT_ID,
        user_id=USER_ID,
        oauth_host_name=OAUTH_HOST,
        private_key_bytes=private_key_bytes,
        expires_in=3600,
        scopes=["signature", "impersonation"],
    )

    api_client.set_default_header(
        "Authorization",
        f"Bearer {token.access_token}",
    )

    envelopes_api = EnvelopesApi(api_client)

    # 2. Prepare the document and signature field.
    doc_content = b"Welcome to Docusign! Please sign this agreement."
    doc_base64 = base64.b64encode(doc_content).decode("utf-8")

    document = Document(
        document_base64=doc_base64,
        name="Welcome Agreement",
        file_extension="txt",
        document_id="1",
    )

    sign_here = SignHere(
        document_id="1",
        page_number="1",
        recipient_id="1",
        x_position="150",
        y_position="150",
    )

    # Omitting client_user_id enables remote signing via email.
    signer = Signer(
        email=SIGNER_EMAIL,
        name=SIGNER_NAME,
        recipient_id="1",
        routing_order="1",
        tabs=Tabs(sign_here_tabs=[sign_here]),
    )

    envelope_definition = EnvelopeDefinition(
        email_subject="Please sign this document from Docusign Quickstart",
        documents=[document],
        recipients=Recipients(signers=[signer]),
        status="sent",
    )

    # 3. Create and send the envelope in one API call.
    summary = envelopes_api.create_envelope(
        account_id=ACCOUNT_ID,
        envelope_definition=envelope_definition,
    )

    print(f"Success! Envelope sent. Envelope ID: {summary.envelope_id}")
    print(f"An email invitation has been sent to {SIGNER_EMAIL}.")


if __name__ == "__main__":
    main()

```

#### Ruby

```ruby
require 'docusign_esign'
require 'base64'

base_path = ENV['DOCUSIGN_BASE_PATH'] ||
  'https://demo.docusign.net/restapi'
oauth_host = ENV['DOCUSIGN_OAUTH_HOST'] ||
  'account-d.docusign.com'

client_id = ENV['DOCUSIGN_CLIENT_ID']
user_id = ENV['DOCUSIGN_USER_ID']
account_id = ENV['DOCUSIGN_ACCOUNT_ID']
key_path = ENV['DOCUSIGN_KEY_FILE'] || 'private.key'
signer_email = ENV['SIGNER_EMAIL'] || 'alex.developer@example.com'
signer_name = ENV['SIGNER_NAME'] || 'Alex Developer'

configuration = DocuSign_eSign::Configuration.new
configuration.host = base_path

# Optional: use your organization's trusted CA bundle.
if ENV['SSL_CERT_FILE']
  configuration.ssl_ca_cert = ENV['SSL_CERT_FILE']
end

api_client = DocuSign_eSign::ApiClient.new(configuration)
api_client.set_oauth_base_path(oauth_host)

# 1. Authenticate using JWT Grant.
private_key = File.read(key_path)

token_response = api_client.request_jwt_user_token(
  client_id,
  user_id,
  private_key,
  3600,
  ['signature', 'impersonation']
)

api_client.default_headers['Authorization'] =
  "Bearer #{token_response.access_token}"

envelopes_api = DocuSign_eSign::EnvelopesApi.new(api_client)

# 2. Prepare the document and signature field.
doc_content = 'Welcome to Docusign! Please sign this agreement.'
doc_base64 = Base64.strict_encode64(doc_content)

document = DocuSign_eSign::Document.new(
  documentBase64: doc_base64,
  name: 'Welcome Agreement',
  fileExtension: 'txt',
  documentId: '1'
)

sign_here = DocuSign_eSign::SignHere.new(
  documentId: '1',
  pageNumber: '1',
  recipientId: '1',
  xPosition: '150',
  yPosition: '150'
)

# Omitting clientUserId enables remote signing via email.
signer = DocuSign_eSign::Signer.new(
  email: signer_email,
  name: signer_name,
  recipientId: '1',
  routingOrder: '1',
  tabs: DocuSign_eSign::Tabs.new(signHereTabs: [sign_here])
)

envelope_definition = DocuSign_eSign::EnvelopeDefinition.new(
  emailSubject: 'Please sign this document from Docusign Quickstart',
  documents: [document],
  recipients: DocuSign_eSign::Recipients.new(signers: [signer]),
  status: 'sent'
)

# 3. Create and send the envelope in one API call.
summary = envelopes_api.create_envelope(
  account_id,
  envelope_definition
)

puts "Success! Envelope sent. Envelope ID: #{summary.envelope_id}"
puts "An email invitation has been sent to #{signer_email}."
```

## Display the signing experience

### Run your example

Run the script from your project directory:

#### Bash

```bash
bash quickstart.sh
```

#### C\#

```bash
dotnet run
```

#### Java

```bash
mvn compile exec:java
```

#### Node.js

```bash
node quickstart.mjs
```

#### PHP

```bash
php quickstart.php
```

#### PowerShell

```powershell
.\quickstart.ps1
```

#### Python

```bash
python quickstart.py
```

#### Ruby

```bash
ruby quickstart.rb
```

### Expected output

Output varies by language. A successful request returns an envelope ID and confirms that the envelope was sent. For example:

```text
Success! Envelope sent. Envelope ID: e81b2f90-1c34-4b48-91ec-29b114d64293
An email invitation has been sent to alex.developer@example.com.
```

### Review and sign

1. Open the email inbox for your recipient.
2. Open the email from Docusign ("Please sign this document from Docusign Quickstart") and select **Review Document**.
3. Complete the signature field and select **Finish**.
4. In your [Docusign Developer Account](https://demo.docusign.net), go to **Manage > Completed** to view the completed agreement.

## Troubleshoot common issues

* **`consent_required` error:** You must grant consent once before your first JWT API call. Open the consent URL from the [Prerequisites section](#prerequisites) in your browser, log in, and select **Accept**.
* **Private key not found:** Verify that `private.key` is saved in your project folder and matches the RSA keypair generated in your Docusign Developer Account under **Apps and Keys**.
* **Invalid User ID or Account ID:** In your developer account, navigate to **Settings > Apps and Keys** and confirm that your User ID (GUID) and API Account ID match the values in your script.
* **Email not received:** Check your spam/junk folder. In the developer demo environment, emails are sent from `dse_demo@docusign.net`.

## Next steps

* **Add Embedded (In-App) Signing:** If your application requires users to sign directly inside your web app rather than via email, add a call to `EnvelopeViews: createRecipient` with a `clientUserId`. See the [Embedded Signing Guide](https://developers.docusign.com/docs/esign-rest-api/esign101/concepts/embedding/).
* **Set Up Webhooks (Docusign Connect):** Configure webhooks to listen for real-time envelope status changes (e.g., completed, declined) on your server instead of relying solely on client-side browser events. Explore setup options in the [Connect Webhooks guide](https://developers.docusign.com/platform/webhooks/connect/).
* **Explore Full API Capabilities:** Review the [eSignature REST API Documentation](https://developers.docusign.com/docs/esign-rest-api/) to learn about additional features like bulk sending, templates, and supplemental documents.
* **Prepare for Production Deployment:** Complete the [Go-Live Process](https://developers.docusign.com/docs/esign-rest-api/go-live/) to promote your integration key from the sandbox environment to production, updating your redirect URIs and base URLs accordingly.