README
View SourceDocuSign 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:
OAuth2 Authorization Code Flow
Interactive walkthrough of the OAuth2 Authorization Code Flow for user-facing applications:
SSL Configuration Example
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!
Installation
The package can be installed by adding docusign
to your list of dependencies in mix.exs
:
def deps do
[
{:docusign, "~> 2.2.2"}
]
end
The docs can be found at https://hexdocs.pm/docusign.
Usage
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
OAuth2 Authorization Code Flow
Recommended for user-facing applications where users need to grant permission for your app to access their DocuSign account.
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 ofprivate_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
- 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)
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/2.2.2"}]
[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 versionUser-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
Configure the underlying Finch connection pools:
config :docusign,
pool_size: 10, # Number of connections per pool (default: 10)
pool_count: 1 # Number of pools (default: 1)
Security Best Practices
- 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
Automatic CA Certificate Detection
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
Tesla adapter configuration
By default, the API is called using Tesla
with the Finch adapter. You can override the adapter
to any Tesla adapter:
config :tesla, adapter: {Tesla.Adapter.Hackney, [recv_timeout: 30_000]}
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
Using Regeneration Scripts
The DocuSign Elixir library can be regenerated using the provided script in the scripts/regen
directory. This script handles:
- Preserving custom functionality (like ModelCleaner)
- Updating generated code from the latest OpenAPI specification
- Adjusting module names and references
- Running tests to verify everything works
To regenerate the library:
- Download the latest OpenAPI specification:
curl -o /tmp/docusign_regen/esignature.swagger.json https://raw.githubusercontent.com/docusign/eSign-OpenAPI-Specification/master/esignature.rest.swagger-v2.1.json
- Generate the client code:
openapi-generator generate -i /tmp/docusign_regen/esignature.swagger.json -g elixir -o /tmp/docusign_regen/elixir_api_client --additional-properties=packageName=docusign_e_signature_restapi
- Run the regeneration script:
cd scripts/regen
chmod +x regenerate_library.sh
./regenerate_library.sh
See the regeneration README for more details.