Unofficial DocuSign Elixir Library used to interact with the eSign REST API.


License
MIT

Documentation

Hex.pm Hexdocs.pm Github.com

DocuSign API Client

Unofficial DocuSign Elixir Library used to interact with the eSignature REST API. Send, sign, and approve documents using this client.

Quick Start with LiveBook

The easiest way to get started is through our interactive LiveBook examples:

Embedded Signing (JWT Impersonation)

Complete working demonstration of DocuSign embedded signing with JWT authentication:

Run in Livebook

OAuth2 Authorization Code Flow

Interactive walkthrough of the OAuth2 Authorization Code Flow for user-facing applications:

Run in Livebook

SSL Configuration Example

Learn how to configure SSL/TLS options for secure connections:

  1. Configure custom CA certificates
  2. Set up client certificate authentication
  3. Understand security best practices
  4. Test your SSL configuration

Run in Livebook

Just click the badges above to run the notebooks in LiveBook - no environment setup required!

Installation

The package can be installed by adding docusign to your list of dependencies in mix.exs:

def deps do
  [
    {:docusign, "~> 3.0.0"}
  ]
end

The docs can be found at https://hexdocs.pm/docusign.

Usage

DocuSign Elixir supports two authentication methods:

  1. OAuth2 Authorization Code Flow - For user-facing applications where users grant permission
  2. JWT Impersonation - For server-to-server applications with pre-configured access

OAuth2 Authorization Code Flow

Recommended for user-facing applications where users need to grant permission for your app to access their DocuSign account.

Using Überauth? Check out ueberauth_docusign for seamless DocuSign OAuth integration with Phoenix applications!

Benefits

  • Users explicitly grant permission through DocuSign's consent screen
  • No admin pre-approval required (unlike JWT impersonation)
  • Tokens can be refreshed without user interaction
  • Standard OAuth2 compliance

Quick Setup

config :docusign,
  hostname: "account-d.docusign.com", # or "account.docusign.com" for production
  client_id: "your_integration_key",
  client_secret: "your_secret_key"

Environment Auto-Detection

Automatically determine the correct OAuth hostname based on your API base URI:

# Automatically detect sandbox vs production from base URI
base_uri = "https://demo.docusign.net/restapi"
hostname = DocuSign.Connection.determine_hostname(base_uri)  # "account-d.docusign.com"

# Configure OAuth with auto-detected hostname
Application.put_env(:docusign, :hostname, hostname)

# Or use the enhanced connection function with auto-detection
{:ok, conn} = DocuSign.Connection.from_oauth_client_with_detection(
  oauth_client,
  account_id: account["account_id"],
  base_uri: account["base_uri"] <> "/restapi",
  auto_detect_hostname: true  # Automatically sets hostname config
)

Usage

# 1. Create OAuth2 client
client = DocuSign.OAuth.AuthorizationCodeStrategy.client(
  redirect_uri: "https://yourapp.com/auth/callback"
)

# 2. Generate authorization URL (redirect user here)
auth_url = OAuth2.Client.authorize_url!(client, scope: "signature")

# 3. Exchange authorization code for tokens (in your callback handler)
client = OAuth2.Client.get_token!(client, code: auth_code_from_callback)

# 4. Get user info and create connection
user_info = DocuSign.OAuth.AuthorizationCodeStrategy.get_user_info!(client)
account = Enum.find(user_info["accounts"], &(&1["is_default"] == "true"))

{:ok, conn} = DocuSign.Connection.from_oauth_client(
  client,
  account_id: account["account_id"],
  base_uri: account["base_uri"] <> "/restapi"
)

# 5. Use connection with DocuSign APIs
{:ok, users} = DocuSign.Api.Users.users_get_users(conn, account["account_id"])

💡 For a complete interactive example, see the OAuth2 LiveBook guide!

JWT Impersonation

For server-to-server applications where you need to act on behalf of users with pre-configured access.

Requirements

  • RSA Private key
  • DocuSign Client ID (integration key)
  • DocuSign Account ID
  • One or more DocuSign User IDs

Note that you can test your integration with the full-featured sandbox environment provided by DocuSign.

Application Configuration

config :docusign,
  hostname: "account-d.docusign.com",
  client_id: "?????-?????-???????",
  private_key_file: "docusign_key.pem"

Notes:

  • Set hostname to account.docusign.com for production
  • Private key path can be relative or absolute
  • Use private_key_contents instead of private_key_file for secrets stored in vault systems

Optional Configuration

config :docusign,
  timeout: 30_000,        # 30 seconds
  token_expires_in: 7_200 # 2 hours

Environment Variables (Recommended)

For security, use environment variables instead of hardcoding credentials:

# .env file
export DOCUSIGN_CLIENT_ID=<client id here>
export DOCUSIGN_PRIVATE_KEY_FILE=<private key file path here>
# config.exs
config :docusign,
  client_id: System.fetch_env!("DOCUSIGN_CLIENT_ID"),
  private_key_file: System.fetch_env!("DOCUSIGN_PRIVATE_KEY_FILE")

DocuSign Setup for JWT

  1. Access DocuSign admin and go to SettingsApps & Keys
  2. Note the API Account ID (this is your Account ID)
  3. Create a new app:
    • Provide a name
    • In Authentication, click + GENERATE RSA
    • Store the private key securely
    • Add redirect URI: https://account-d.docusign.com/me (sandbox) or https://account.docusign.com/me (production)
  4. Note the Integration Key (this is your Client ID)

User Consent for Impersonation

For impersonating other users, they must first consent by visiting:

Sandbox:

https://account-d.docusign.com/oauth/auth?response_type=code&scope=signature%20impersonation&client_id=YOUR_CLIENT_ID&redirect_uri=https://account-d.docusign.com/me

Production:

https://account.docusign.com/oauth/auth?response_type=code&scope=signature%20impersonation&client_id=YOUR_CLIENT_ID&redirect_uri=https://account.docusign.com/me

Using JWT APIs

# Establish connection
user_id = "USER_ID"
{:ok, conn} = DocuSign.Connection.get(user_id)

# Call APIs
account_id = "ACCOUNT_ID"
{:ok, users} = DocuSign.Api.Users.users_get_users(conn, account_id)

Request/Response Debugging

The DocuSign Elixir client provides comprehensive debugging capabilities for HTTP requests and responses, similar to the Ruby client's debugging features.

Enable Debugging

Enable debugging in your configuration to log HTTP request/response details:

config :docusign, debugging: true

Or enable it at runtime:

DocuSign.Debug.enable_debugging()

Debug Output

When debugging is enabled, you'll see detailed logs including:

  • HTTP request method, URL, and timing
  • Request and response headers (with sensitive data filtered)
  • Request and response bodies
  • SDK identification headers

Example debug output:

[debug] GET https://demo.docusign.net/restapi/v2.1/accounts -> 200 (145.2 ms)
[debug] Request headers: [{"authorization", "[FILTERED]"}, {"X-DocuSign-SDK", "Elixir/3.0.0"}]
[debug] Response body: {"accounts": [...]}

Header Filtering

Sensitive headers like authorization tokens are automatically filtered in debug logs. You can customize which headers to filter:

config :docusign, :debug_filter_headers, ["authorization", "x-api-key", "x-custom-secret"]

SDK Identification

The client automatically includes SDK identification headers with all requests:

  • X-DocuSign-SDK: Elixir/2.2.2 - Identifies the SDK and version
  • User-Agent: DocuSign-Elixir/2.2.2 - Standard user agent header

These headers help DocuSign track API usage and provide better support.

Configuration Options

config :docusign,
  debugging: true,                    # Enable/disable debug logging
  debug_filter_headers: [             # Headers to filter in logs
    "authorization",
    "x-api-key"
  ]

Timeout configuration

By default, HTTP requests will time out after 30_000 ms. You can configure the timeout:

config :docusign, timeout: 60_000

Structured Error Handling

The DocuSign Elixir client provides opt-in structured error handling, returning detailed error structs instead of generic tuples for API failures. This allows for more granular and robust error management in your application.

Enable Structured Errors

To enable structured errors, set the :structured_errors option in your application configuration:

config :docusign, :structured_errors, true

When enabled, API calls that result in an error (e.g., HTTP status codes 4xx or 5xx) will return an {:error, error_struct} tuple, where error_struct is one of the following:

  • DocuSign.ApiError: A general API error.
  • DocuSign.AuthenticationError: Specifically for 401 Unauthorized errors.
  • DocuSign.RateLimitError: Specifically for 429 Too Many Requests errors.
  • DocuSign.ValidationError: Specifically for 400 Bad Request errors.

Each error struct contains message, status, and body fields, providing comprehensive details about the error.

Example Usage

case DocuSign.Api.Envelopes.envelopes_get_envelope(conn, account_id, envelope_id) do
  {:ok, envelope} ->
    IO.puts("Envelope retrieved: #{envelope.status}")
  {:error, %DocuSign.AuthenticationError{message: msg, status: status}} ->
    IO.puts("Authentication failed (Status: #{status}): #{msg}")
  {:error, %DocuSign.ValidationError{message: msg, body: body}} ->
    IO.puts("Validation error: #{msg}. Details: #{inspect(body)}")
  {:error, %DocuSign.ApiError{message: msg, status: status}} ->
    IO.puts("API Error (Status: #{status}): #{msg}")
  {:error, reason} ->
    IO.puts("An unexpected error occurred: #{inspect(reason)}")
end

If :structured_errors is false (the default), errors will continue to be returned as {:error, {:http_error, status, body}} tuples for backward compatibility.

SSL/TLS Configuration

The DocuSign client supports comprehensive SSL/TLS configuration for secure connections. This is particularly useful for:

  • Using custom CA certificates
  • Client certificate authentication (mutual TLS)
  • Controlling SSL verification behavior
  • Configuring cipher suites and TLS versions

Basic SSL Configuration

Configure SSL options at the application level:

config :docusign, :ssl_options,
  verify: :verify_peer,                    # Always verify server certificates (default)
  cacertfile: "/path/to/ca-bundle.crt",   # Custom CA certificate bundle
  depth: 3                                 # Certificate chain verification depth

Client Certificate Authentication

For mutual TLS authentication:

config :docusign, :ssl_options,
  certfile: "/path/to/client-cert.pem",   # Client certificate
  keyfile: "/path/to/client-key.pem",     # Client private key
  password: "keypassword"                  # Password for encrypted key (if needed)

Advanced SSL Options

config :docusign, :ssl_options,
  # TLS versions
  versions: [:"tlsv1.2", :"tlsv1.3"],

  # Cipher suites (example)
  ciphers: [
    "ECDHE-RSA-AES256-GCM-SHA384",
    "ECDHE-RSA-AES128-GCM-SHA256"
  ],

  # Custom hostname verification
  customize_hostname_check: [
    match_fun: :public_key.pkix_verify_hostname_match_fun(:https)
  ],

  # Custom verification function
  verify_fun: {&MyApp.SSLVerification.verify/3, nil}

Per-Request SSL Options

You can override SSL options for specific requests:

{:ok, conn} = DocuSign.Connection.get(user_id)

# Use custom CA certificate for this request only
DocuSign.Connection.request(conn,
  method: :get,
  url: "/accounts",
  ssl_options: [
    cacertfile: "/special/ca.pem",
    verify: :verify_peer
  ]
)

Connection Pooling

Optimize HTTP connections for high-throughput applications:

# Enable connection pooling with custom configuration
config :docusign, :pool_options, [
  size: 50,               # Number of connections per pool (default: 10)
  count: 2,               # Number of pools for concurrency (default: 1)
  max_idle_time: 600_000, # Keep connections alive for 10 minutes (default: 5 minutes)
  timeout: 30_000         # Connection timeout in ms (default: 30 seconds)
]

Benefits of connection pooling:

  • Connection reuse - Reduces overhead of establishing new HTTPS connections
  • Better throughput - Multiple pools allow concurrent request handling
  • Resource management - Automatic cleanup of idle connections
  • Performance monitoring - Track pool health with DocuSign.ConnectionPool.health()

Example usage:

# Check if pooling is enabled
DocuSign.ConnectionPool.enabled?()
#=> true

# Get current pool configuration
DocuSign.ConnectionPool.config()
#=> %{size: 50, count: 2, max_idle_time: 600_000, timeout: 30_000, enabled: true}

# Monitor pool health
{:ok, health} = DocuSign.ConnectionPool.health()
#=> {:ok, %{status: :healthy, message: "Connection pooling is active", config: %{...}}}

Security Best Practices

  1. Always use :verify_peer in production - Never disable certificate verification in production environments
  2. Keep CA certificates updated - Ensure your CA bundle includes all necessary root certificates
  3. Use strong cipher suites - Configure only secure cipher suites
  4. Enable hostname verification - Always verify that the certificate matches the hostname

Automatic CA Certificate Detection

If you don't specify CA certificates, the library will attempt to use them in this order:

  1. User-specified :cacertfile or :cacerts
  2. CAStore library (if available as a dependency)
  3. System CA certificates from common locations
  4. Erlang's built-in CA certificates as a fallback

HTTP Client Configuration

The library uses Req with Finch as the underlying HTTP client. Req automatically manages its own Finch instance named Req.Finch.

Advanced Configuration

To configure advanced HTTP options (connection pools, timeouts, etc.), you can configure the global Req.Finch instance:

config :req, :finch_options, [
  pools: %{
    :default => [size: 50, count: 1],
    "https://demo.docusign.net" => [size: 10, count: 2]
  }
]

See the Finch documentation for all available options.

Retry Configuration

The client includes automatic retry logic for transient failures. By default, it retries up to 3 times with exponential backoff.

Configuration

config :docusign, :retry_options,
  max_retries: 3,         # Maximum retry attempts (default: 3)
  backoff_factor: 2,      # Exponential backoff multiplier (default: 2)
  max_delay: 30_000       # Maximum delay between retries in ms (default: 30_000)

# Disable retries entirely
config :docusign, :retry_options, enabled: false

The client automatically handles rate limits (429 responses) by honoring the Retry-After header when present.

Telemetry and Monitoring

The DocuSign client emits telemetry events for observability and monitoring. These events allow you to track API performance, error rates, and usage patterns.

Available Events

The following telemetry events are emitted:

  • [:docusign, :api, :start] - Fired when an API call begins
  • [:docusign, :api, :stop] - Fired when an API call completes successfully
  • [:docusign, :api, :exception] - Fired when an API call fails
  • [:docusign, :rate_limit, :hit] - Fired when rate limited by DocuSign

Additionally, since the client uses Finch for HTTP, you also get:

  • [:finch, :request, :start] - HTTP request started
  • [:finch, :request, :stop] - HTTP request completed

Basic Usage

Attach handlers to telemetry events:

:telemetry.attach(
  "log-docusign-requests",
  [:docusign, :api, :stop],
  fn _event, measurements, metadata, _config ->
    IO.puts("API call to #{metadata.operation} took #{measurements.duration / 1_000_000}ms")
  end,
  nil
)

Integration with Telemetry.Metrics

For production monitoring with tools like LiveDashboard:

defmodule MyApp.Telemetry do
  import Telemetry.Metrics

  def metrics do
    [
      # API performance
      summary("docusign.api.duration",
        unit: {:native, :millisecond},
        tags: [:operation, :status]
      ),

      # Request counts
      counter("docusign.api.count", tags: [:operation]),

      # Error rates
      counter("docusign.api.exception.count", tags: [:operation])
    ]
  end
end

See DocuSign.Telemetry module documentation for complete details.

DocuSign Connect

To receive webhooks from DocuSign Connect, you can use DocuSign.WebhookPlug with your custom webhook handler. See the documentation of DocuSign.WebhookPlug for more details.

Migration Guide

For information about migrating between versions, please see MIGRATING.md.

Regenerating the Library

The DocuSign Elixir library can be regenerated from the latest OpenAPI specification using the provided scripts.

cd scripts/regen
./regenerate_library.sh

See the regeneration README for details.