[](https://hex.pm/packages/docusign)
[](https://hexdocs.pm/docusign)
[](https://github.com/neilberkman/docusign_elixir/actions)
# 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:
[](https://livebook.dev/run?url=https://github.com/neilberkman/docusign_elixir/blob/main/examples/embedded_signing.livemd)
### OAuth2 Authorization Code Flow
Interactive walkthrough of the OAuth2 Authorization Code Flow for user-facing applications:
[](https://livebook.dev/run?url=https://github.com/neilberkman/docusign_elixir/blob/main/examples/oauth_authorization_code_flow.livemd)
### 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
[](https://livebook.dev/run?url=https://github.com/neilberkman/docusign_elixir/blob/main/examples/ssl_configuration.livemd)
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`:
```elixir
def deps do
[
{:docusign, "~> 3.0.0"}
]
end
```
The docs can be found at [https://hexdocs.pm/docusign](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.
#### 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
```elixir
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:
```elixir
# 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
```elixir
# 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](https://livebook.dev/run?url=https://github.com/neilberkman/docusign_elixir/blob/main/examples/oauth_authorization_code_flow.livemd)!**
### 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](https://appdemo.docusign.com).
#### Application Configuration
```elixir
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
```elixir
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:
```bash
# .env file
export DOCUSIGN_CLIENT_ID=<client id here>
export DOCUSIGN_PRIVATE_KEY_FILE=<private key file path here>
```
```elixir
# 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 **Settings** → **Apps & 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
```elixir
# 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:
```elixir
config :docusign, debugging: true
```
Or enable it at runtime:
```elixir
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:
```elixir
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
```elixir
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:
```elixir
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:
```elixir
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
```elixir
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:
```elixir
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:
```elixir
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
```elixir
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:
```elixir
{: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:
```elixir
config :docusign,
pool_size: 10, # Number of connections per pool (default: 10)
pool_count: 1 # Number of pools (default: 1)
```
### 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](https://hexdocs.pm/req) with [Finch](https://hexdocs.pm/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:
```elixir
config :req, :finch_options, [
pools: %{
:default => [size: 50, count: 1],
"https://demo.docusign.net" => [size: 10, count: 2]
}
]
```
See the [Finch documentation](https://hexdocs.pm/finch/Finch.html#start_link/1) for all available options.
## 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](MIGRATING.md).
## Regenerating the Library
The DocuSign Elixir library can be regenerated from the latest OpenAPI specification using the provided scripts.
```bash
cd scripts/regen
./regenerate_library.sh
```
See the [regeneration README](scripts/regen/README.md) for details.