Unofficial DocuSign Elixir Library used to interact with the eSignature REST API. Send, sign, and approve documents using this client.
The easiest way to get started is through our interactive LiveBook examples:
Complete working demonstration of DocuSign embedded signing with JWT authentication:
Interactive walkthrough of the OAuth2 Authorization Code Flow for user-facing applications:
Learn how to configure SSL/TLS options for secure connections:
- Configure custom CA certificates
- Set up client certificate authentication
- Understand security best practices
- Test your SSL configuration
Just click the badges above to run the notebooks in LiveBook - no environment setup required!
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.
DocuSign Elixir supports two authentication methods:
- OAuth2 Authorization Code Flow - For user-facing applications where users grant permission
- JWT Impersonation - For server-to-server applications with pre-configured access
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!
- 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
config :docusign,
hostname: "account-d.docusign.com", # or "account.docusign.com" for production
client_id: "your_integration_key",
client_secret: "your_secret_key"
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
)
# 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!
For server-to-server applications where you need to act on behalf of users with pre-configured access.
- 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.
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 ofprivate_key_file
for secrets stored in vault systems
config :docusign,
timeout: 30_000, # 30 seconds
token_expires_in: 7_200 # 2 hours
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")
- Access DocuSign admin and go to Settings → Apps & Keys
- Note the API Account ID (this is your Account ID)
- 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) orhttps://account.docusign.com/me
(production)
- Note the Integration Key (this is your Client ID)
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
# 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)
The DocuSign Elixir client provides comprehensive debugging capabilities for HTTP requests and responses, similar to the Ruby client's debugging features.
Enable debugging in your configuration to log HTTP request/response details:
config :docusign, debugging: true
Or enable it at runtime:
DocuSign.Debug.enable_debugging()
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": [...]}
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"]
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.
config :docusign,
debugging: true, # Enable/disable debug logging
debug_filter_headers: [ # Headers to filter in logs
"authorization",
"x-api-key"
]
By default, HTTP requests will time out after 30_000 ms. You can configure the timeout:
config :docusign, timeout: 60_000
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.
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.
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.
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
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
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)
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}
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
]
)
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: %{...}}}
-
Always use
:verify_peer
in production - Never disable certificate verification in production environments - Keep CA certificates updated - Ensure your CA bundle includes all necessary root certificates
- Use strong cipher suites - Configure only secure cipher suites
- Enable hostname verification - Always verify that the certificate matches the hostname
If you don't specify CA certificates, the library will attempt to use them in this order:
- User-specified
:cacertfile
or:cacerts
- CAStore library (if available as a dependency)
- System CA certificates from common locations
- Erlang's built-in CA certificates as a fallback
The library uses Req with Finch as the underlying HTTP client.
Req automatically manages its own Finch instance named Req.Finch
.
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.
The client includes automatic retry logic for transient failures. By default, it retries up to 3 times with exponential backoff.
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.
The DocuSign client emits telemetry events for observability and monitoring. These events allow you to track API performance, error rates, and usage patterns.
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
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
)
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.
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.
For information about migrating between versions, please see MIGRATING.md.
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.