Automate Document Signing using the DocuSign API
Adam Yue
Jan. 8, 2023
/
DocuSign is a widely used document signing service. It provides an SDK for a range of programming languages that can achieve the automation of creating, sending and signing documents. The API provides more flexibility compare to using the DocuSign web portal, but requires a deeper understanding how DocuSign "thinks" about document signing. In this blog, we will go through how to use the DocuSign SDK in a Kotlin project with several common use cases.
If you want to reproduce the following demo by yourself, please create a DocuSign developer account from here and upload some testing files as the templates. These IDs and credentials are required to send an Envelope:
accountId: your account ID which can be found in “General Settings”
userId: You can have multiple users under your DocuSign account. You can create users and find their id on “Settings -> Users”
clientId (Integration key): Is a UUID can be obtained in your developer account by accessing the “Settings -> Apps and Integration Keys” page
client secret (RSA private key): This is for the integration key you obtained above. It is also created on the “Apps and Integration Keys” page. The client secret can only be copied the first time it is displayed.
templateId: UUID of template files that can be found in editing templates page.
basePath: is https://demo.docusign.net/restapi for the DocuSign development environment
Let’s start with an example to demonstrate how to create and send an envelope using the DocuSign SDK:
Kotlin
// Server template
val demoTemplate = ServerTemplate()
demoTemplate.sequence = "1"
demoTemplate.templateId = "cd635318-aaad-4e63-ba2d-adb9edf06db3"
The SignHere object represents the location where a recipient has to sign the envelope. To determine the correct x and y position you can use the web portal UI.
``` Kotlin
// singer's tab
val signerTab = Tabs()
val signHere = SignHere()
signHere.pageNumber = "1"
signHere.documentId = "1"
signHere.xPosition = "125"
signHere.yPosition = "327"
signerTab.signHereTabs = listOf(signHere)
```
The Signer object is the signer's configuration which includes their email, name, recipientId and routingOrder. The recipientId is a unique string, you can use any string other than “1”. routingOrder defines the signing sequence. If there are two signers, the signer with smaller number will sign first.
```Kotlin
// signer's configuration
val signer = Signer()
signer.tabs(signerTab)
signer.email = signerEmail
signer.name = "Test Signer"
signer.recipientId = "1"
signer.routingOrder = "1"
```
Assign the signers to the Recipients object.
```Kotlin
//config recipients
val recipients = Recipients()
val signers = mutableListOf<Signer>()
signers.add(signer)
recipients.signers = signers
```
Create InlineTemplate with Recipients
Kotlin
val recipientsTemplate = InlineTemplate()
recipientsTemplate.sequence = "1"
recipientsTemplate.recipients = recipients
Create CompositeTemplate using both InlineTemplate and ServerTemplate
Kotlin
// composite template
val compositeTemplate = CompositeTemplate()
compositeTemplate.compositeTemplateId = "1"
compositeTemplate.serverTemplates = listOf(demoTemplate)
compositeTemplate.inlineTemplates = listOf(recipientsTemplate)
Create EnvelopeDefinition
Kotlin
val envelopeDefinition = EnvelopeDefinition()
envelopeDefinition.emailSubject = "DecoSignDemo"
envelopeDefinition.compositeTemplates = listOf(compositeTemplate)
An EnvelopeDefinition is the core object which combines the templates (actual files) and signers together. This diagram outlines the EnvelopeDefinition object structure:
DocuSign provides a variety of commonly used tabs such as: text, checkbox, dropdown etc. which can be added via Tab class in the SDK. For example: Let's add a text tab to capture the envelope’s creation date:
val formatter = DateTimeFormatter.ofPattern("dd MMMM yyyy")
val date = Text()
date.documentId = "1"
date.pageNumber = "1"
date.value =
Instant.ofEpochMilli(System.currentTimeMillis())
.atZone(ZoneId.systemDefault()).toLocalDate().format(formatter)
date.xPosition = "100"
date.yPosition = "354"
date.fontSize = "Size12"
val textTabs = listOf(date)
val prefilledTabs = PrefillTabs()
prefilledTabs.textTabs = textTabs
val tabs = Tabs()
tabs.prefillTabs = prefilledTabs
Note: To send an envelope, Tabs are always required. If no tabs are required, then create an empty Tabs object
After the EnvelopeDefinition and Tabs are prepared, the envelope is ready for sending.
Create the ApiClient(check how to get basePath, clientId, userId, privateKey at Preparation):
```kotlin val apiClient = ApiClient(basePath) val jwtUserToken = apiClient.requestJWTUserToken( clientId, userId, listOf(OAuth.Scope_SIGNATURE), privateKey.toByteArray(Charsets.UTF_8), 300 )
apiClient.setAccessToken(jwtUserToken.accessToken, 300) ```
Create the EnvelopesApi
kotlin
val envelopesApi = EnvelopesApi(docuSignClient)
Finally, Send the envelope with tabs: ```kotlin val envelopeSummary = envelopesApi.createEnvelope(accountId, envelopeDefinition)
val tabs = fillTabs() envelopesApi.createDocumentTabs(accountId,envelopeSummary.envelopeId,"1",tabs) ```
From the re-usability and management point of view, instead of having a big fixed template, it can be split into several small templates. It’s also one of the Best practices for using composite templates: “Create DocuSign templates around individual documents, rather than bundling multiple documents in a single template”
See Step One 'Creating the Envelope Definition' Part 5
envelopeDefinition.compositeTemplates = listOf(compositeTemplate)
An Envelope definition can take a list of composite templates, we can create another composite template (composite template Id must be different) in the same way as part 1 to 4 and add it to the list:
envelopeDefinition.compositeTemplates = listOf(
compositeTemplate, secondCompositeTemplate)
The page number always points to the composite template page on which tabs will be added. For example, a singer A needs to sign on both compositeTemplate and a second CompositeTemplate above, both templates only have one page. When creating a Sign Here tab for the second composite template, the page number is still “1” even it’s the second page of the final envelope.
If there are multiple signers, each signer should have a unique recipient Id. They may have the same Routing Order, which means all signers will receive the envelope at the same time. If some signers are preferred to sign first, they should have smaller routing order.
This blog demonstrates the basic usage of the DocuSign SDK. DocuSign is a powerful document signing tool. It can greatly simplify business services processes. If you want to find out how Document Signing can help your organization to improve productivity don't hesitate to contact us.
...
private fun createEnvelope(): EnvelopeDefinition {
// Server template
val demoTemplate = ServerTemplate()
demoTemplate.sequence = "1"
demoTemplate.templateId = "cd635318-aaad-4e63-ba2d-adb9edf06db3"
// singer's tab
val signerTab = Tabs()
val signHere = SignHere()
signHere.pageNumber = "1"
signHere.documentId = "1"
signHere.xPosition = "125"
signHere.yPosition = "327"
signerTab.signHereTabs = listOf(signHere)
// config signer
val signer = Signer()
signer.tabs(signerTab)
signer.email = signerEmail
signer.name = "Test Signer"
signer.recipientId = "1"
signer.routingOrder = "1"
//config recipients
val recipients = Recipients()
val signers = mutableListOf<Signer>()
signers.add(signer)
recipients.signers = signers
// inline template
val recipientsTemplate = InlineTemplate()
recipientsTemplate.sequence = "1"
recipientsTemplate.recipients = recipients
// composite template
val compositeTemplate = CompositeTemplate()
compositeTemplate.compositeTemplateId = "1"
compositeTemplate.serverTemplates = listOf(demoTemplate)
compositeTemplate.inlineTemplates = listOf(recipientsTemplate)
// envelope
val envelopeDefinition = EnvelopeDefinition()
envelopeDefinition.emailSubject = "DecoSignDemo"
envelopeDefinition.compositeTemplates = listOf(compositeTemplate)
envelopeDefinition.status = "sent"
return envelopeDefinition
}
private fun fillTabs(): Tabs {
val formatter = DateTimeFormatter.ofPattern("dd MMMM yyyy")
val date = Text()
date.documentId = "1"
date.pageNumber = "1"
date.value =
Instant.ofEpochMilli(System.currentTimeMillis())
.atZone(ZoneId.systemDefault()).toLocalDate().format(formatter)
date.xPosition = "100"
date.yPosition = "354"
date.fontSize = "Size12"
val textTabs = listOf(date)
val prefilledTabs = PrefillTabs()
prefilledTabs.textTabs = textTabs
val tabs = Tabs()
tabs.prefillTabs = prefilledTabs
return tabs
}
private fun createDocuSignApiClient(): ApiClient {
val apiClient = ApiClient(basePath)
val jwtUserToken = apiClient.requestJWTUserToken(
clientId,
userId,
listOf(OAuth.Scope_SIGNATURE),
privateKey.toByteArray(Charsets.UTF_8),
300
)
apiClient.setAccessToken(jwtUserToken.accessToken, 300)
return apiClient
}
@Test
fun sendEnvelope(){
val docuSignClient = createDocuSignApiClient()
val envelopesApi = EnvelopesApi(docuSignClient)
val envelopeDefinition = createEnvelope()
val envelopeSummary = envelopesApi.createEnvelope(accountId, envelopeDefinition)
val tabs = fillTabs()
envelopesApi.createDocumentTabs(
accountId,
envelopeSummary.envelopeId,
"1",
tabs
)
println("envelope has been sent.")
}
...
Address
Level 8
11-17 York Street
Sydney NSW 2000
Phone Number
+61 2 8294 8067
Email
[email protected]
By Adam Yue
By Felix Schmitz
© 2017-2025 Darumatic Pty Ltd. All Rights Reserved.