Recently, my team at January Advisors built a legal services web application that needed document management capabilities. We wanted to be able to store and edit information about applicants, and we also wanted users to be able to generate, update, and send signable documents. DocuSign already offers extensive document management and e-signature capabilities, so we decided to pursue DocuSign integration for this functionality.
DocuSign’s eSignature API provides a variety of quickstart templates and documentation for using the API with Node.js. However, our web application used React.js for the client and Express.js for the server, and we couldn’t find anything cohesive that quite fit our needs. After piecing the documentation together, and some trial and error, we found something that worked for us. Hopefully this blog series helps you integrate similarly structured applications with DocuSign!
The first part of this blog will focus on authorization and how to secure API requests made to DocuSign. It will take place entirely on the server side using Express.js.
Selecting an authorization flow
DocuSign has a number of OAuth 2.0 flows that can be used to secure API requests made to DocuSign. Which flow you use depends on what your desired integration looks like.
According to DocuSign’s documentation, if your users will use their individual DocuSign logins, you must use the Authorization Code Grant or Implicit Grant. If you use a single DocuSign login for all users in your app, you should use the JWT Grant.

For our purposes, users needed the ability to generate, update, and send signable documents directly from our web application. We wanted users to have unique logins for our application, but on the DocuSign end, we wanted all documents to be managed and sent from one DocuSign account. Below is a high level diagram of the desired flow.

Since we wanted to use a single DocuSign login for all application users the JWT Grant flow was the best approach.
To expand a bit more on why the JWT Grant flow is the best fit, here is some additional information from the DocuSign documentation:
JSON Web Token (JWT) Grant is an OAuth 2.0 flow that is used to grant an access token to service integrations. Service integrations differ from user integrations (which authenticate through the Authorization Code and Implicit grant flows) in that:
- A service integration integrates directly with a DocuSign account and does not authenticate every end user. Instead, the integration obtains permission to impersonate (act as) a specific user on a long-term basis without that user being present. This differs from acting on behalf of a user (as in the Authorization Code and Implicit Grants) in that it does not require the user to be present and logged in. For example, a service integration might use an HR alias or manager’s account credentials to send onboarding documents whenever a new employee is hired by a company.
- Service integrations are often heavily automated, and will frequently call the DocuSign platform without direct user interaction. For example, the service integration mentioned above would likely automate the process of sending the onboarding documents so that the HR alias or manager would not need to explicitly send the documents each time a new employee was hired.
Implementing the JWT Grant authorization flow for DocuSign integration
Ok, so how does JWT Grant authorization work? Once again, I will defer to DocuSign’s documentation:
In the JWT Grant authorization flow, your integration posts a JWT to the DocuSign authentication service, asserting its credentials and providing the data of the user that it wishes to impersonate. DocuSign validates that the assertion is signed and that your integration has the consent to act on behalf of the user, then issues an access token that allows you to securely call DocuSign APIs. Consent to act on behalf of a user is given through a consent process.
The diagram below illustrates how this works in the context of our application. You can see in the middle of the diagram that our application requests the access token (by posting a JWT), DocuSign grants the access token, and then our application uses the token to make subsequent requests.

In the remainder of this blog post, I will walk you through how to implement this flow. I have broken the implementation down into four steps:
- Collect information and provide application consent.
- Request an access token by posting a JWT to the DocuSign authentication service.
- Retrieve and store the access token.
- Make a request to DocuSign using the access token.
1. Collect information and provide application consent
In order to set up the JWT grant authorization method, you will need to collect a few pieces of information and provide consent for impersonation. Luckily, DocuSign’s documentation for these two pieces are great, so I will simply refer you to them!
The How to get an access token with JWT Grant guide (prerequisites and step 1–4) will walk you through collecting information and providing consent. The Node.js SDK authentication guide is also a helpful reference, and provides a bit more information on what each piece of information is.
Note: One piece of information you will need to collect is an RSA key. In order to format the RSA key properly in your .env file, you will need to update carriage returns to string literals (“\n”). You can do this by saving the key in a text file and then running the following command in a terminal: awk -v ORS=’\\n’ ‘1’ filename
, then copy the output from your terminal (minus the “\n” at the end of the output). Be sure to include quotation marks around your key.
2. Request an access token by posting a JWT to the DocuSign authentication service.
Now that you’ve collected your information and provided application consent, you will need to request an access token. In order to request an access token, you need to generate a JWT using the information you collected, define which permissions you want the access token to grant, and include both of these components in your request. This is made fairly simple by the DocusSign eSignature Node Client SDK.
First, install the DocusSign eSignature Node Client SDK.
const docusign = require('docusign-esign')
Using the SDK, instantiate an ApiClient
object and set the OAuth base path to the OAuth server domain (DOCUSIGN_OATH_SERVER
). For the developer environment, the domain is “account-d.docusign.com” and for the production environment, the domain is “account.docusign.com”. The ApiClient
object will be used to make eSignature API calls.
let docusignClient = new docusign.ApiClient() docusignClient.setOAuthBasePath(DOCUSIGN_OATH_SERVER)
Next, define parameters.
First, define which OAuth scopes (permissions) we want the access token to grant. “Signature” is the scope needed for eSignature REST API methods. “Impersonation” is the scope needed to impersonate the user who consented to impersonation in step 1.
const docusignScopes = [ 'signature', 'impersonation' ]
Second, set the access token life in seconds to the max limit allowed, 60 minutes. jwtLifeSeconds
will be used to generate the access token. jwtExpiration
will be returned at the end of this function and used later.
// 60 minutes (max is 60 minutes) const jwtLifeSeconds = 60 * 60 const jwtLifeMilliseconds = jwtLifeSeconds * 1000 const jwtExpiration = Date.now() + jwtLifeMilliseconds
Once you have defined your parameters, you can use the docusign.ApiClient
requestJWTUserToken
function and the information gathered in step 1 to generate an access token and return it.
In the code below, DOCUSIGN_JWT_CLIENT_ID
is the integration key, DOCUSIGN_IMPERSONATED_USER_GUID
is the user ID, and DOCUSIGN_RSA_KEY
is the DocuSign private key.
const docusignResponse = await docusignClient.requestJWTUserToken(DOCUSIGN_JWT_CLIENT_ID, DOCUSIGN_IMPERSONATED_USER_GUID, docusignScopes, DOCUSIGN_RSA_KEY, jwtLifeSeconds) return { docusignAccessToken: docusignResponse.body.access_token, docusignAccessTokenExpiration: jwtExpiration }
Finally, combine all of this into a requestDocusignAccessToken
function.
You can view DocuSign’s official documentation here: https://developers.docusign.com/docs/esign-rest-api/sdk-tools/node/auth/
3. Store and refresh the access token.
In the previous step, you wrote a function to retrieve an access token from DocuSign. However, this access token expires after one hour, and no refresh token is provided. DocuSign provides best practices for handling the token:
Best practices for managing access tokens:
- Do cache the access token until it expires or is about to expire.
- Do not create a new access token for each API call.
- Recommended: Check the expiration time of the access token before you use it. If it has expired or is about to expire (within 10 minutes), then obtain a new authorization token before making the API call. If using a refresh token, check that it hasn’t expired as well.
For this setup, you will store the access token and expiration on a session. I recommend using a store such as Redis, but for now you can use the default express-session
store.
Now that you have configured express-session
, you’ll write a middleware function that retrieves and stores a new access token as needed.
To store the access token and expiration on a session, use the requestDocusignAccessToken
function you wrote in step 2 and save the returned values to req.session
.
const { docusignAccessToken, docusignAccessTokenExpiration } = await requestDocusignAccessToken() req.session.docusignAccessToken = docusignAccessToken req.session.docusignAccessTokenExpiration = docusignAccessTokenExpiration
You only want to use requestDocusignAccessToken
when there’s not already a token stored on a session, so check if a token exists.
if (req.session.docusignAccessToken)
If a token exists, check to see if the token has expired. You should also add a buffer of two minutes (DocuSign recommends ten) to the expiration to ensure the token doesn’t expire between the time you check the expiration and use the token.
if (req.session.docusignAccessTokenExpiration - (1000 * 60 * 2) > Date.now())
Now, combine all of this into a checkDocusignAccessToken
function.
Finally, you’ll use this function as middleware in any route that utilizes the DocuSign API and requires an access token. In the example below, checkDocusignAccessToken
is used in a route that creates a new DocuSign envelope. Now, every time the /create-sample-envelope
endpoint is hit, checkDocusignAccessToken
will check for a valid access token, and if there isn’t one, it will retrieve and store a new one.
One final note. You may have noticed another piece of middleware in the code above, router.use(checkIsLoggedIn).
Even though we don’t need each user to have their own DocuSign login, we still want them to be logged into our web application to have access to this functionality.
4. Make a request to DocuSign using the access token.
Now that you have a token, you can use it to make requests through the DocuSign API (yay!). DocuSign has an SDK that you can use to make requests, but we ran into a number of bugs while using it, so we used axios to make requests instead. Below is an example of a request to create a new envelope in DocuSign.
The request includes a few key pieces.
url: ${DOCUSIGN_API_BASE}/accounts/${DOCUSIGN_API_ACCOUNT_ID}/envelopes
First is the url
. All requests will start with DOCUSIGN_API_BASE
, the API base URL we collected in step 1. DOCUSIGN_API_ACCOUNT_ID
, the API account ID we collected in step 1, is also included to indicate the user on whose behalf the application is making the API call. The rest of the url, “envelopes”, indicates that this request is to create a new envelope.
data: { // see part 2 of blog }
Second is the data
. This is where you pass parameters for the request, such as the recipients for the envelope or the documents to be signed. We’ll discuss this more in part 2 of this blog.
headers: { Authorization': `Bearer ${req.session.docusignAccessToken} }
Third are the headers
, specifically the Authorization
header. This is how the access token you retrieved in the previous step is included in the DocuSign request. A valid token that has not expired must be included to receive a successful response.
And that’s it!
To recap, you retrieved an access token from DocuSign using JWT Grant authorization, stored the access token on a session so that you could reuse it, checked to make sure the access token was still valid before each DocuSign request and requested a new one of it was not, and included the access token on every DocuSign request.
In the next part of this series, I’ll show you how we used the DocuSign API to dynamically create, edit, and preview envelopes through a web application.