From the Trenches: Working with authoritative copies using Docusign APIs
An authoritative copy is the single, distinct, absolute original version of a document that is unique, identifiable, and unalterable without detection. Note that since all electronic files are “copies” and don’t intrinsically have a uniqueness property, authoritative copy requires the application of management and control processes. Within Docusign, you can specify whether all or just specific documents in an envelope are covered by the authoritative copy controls. Whether a document is considered an authoritative copy depends on the values of three properties in the envelope
and document
objects. The overall process, referred to as envelope vaulting, is a series of actions that lead to control of an authoritative document being transferred away from an eSignature provider to an outside party.
This system is ideal for when contract documents themselves have a tangible value. For example, a loan document or other form of chattel paper may require the owner to maintain an authoritative copy control or transfer when selling their interest in the loan to another party.
What are the primary differences in Authoritative Copy between the eSignature REST vs. SOAP APIs?
Within the Docusign platform, our Authoritative Copy APIs work on a combination of two platforms, depending on the Authoritative operations in use. Both the eSignature SOAP and REST APIs support specifying authoritative copy controls on envelope documents. However, only the SOAP API provides operations necessary for exporting Authoritative Copy-controlled documents out of Docusign control.
The primary operations and attributes per API are:
eSignature SOAP API:
Authoritative Copy in SOAP requires two features to be enabled for your account. The first is called Auto Authoritative Copy. This feature is meant to automatically enable envelopes for Authoritative Copy by flagging all outgoing envelopes and associated documents as authoritative. The second, Can Export Authoritative Copies, is an individual user permission to allow export of authoritative documents.
eSignature REST API v2.1
To enable Authoritative Copy at the envelope level, set the authoritativeCopy
flag to true
and flag individual documents for Authoritative Copy.
Parameters: | Description |
---|---|
Envelope: authoritativeCopy |
Enables or disables the feature. If this value is false , no documents are authoritative copies. |
Envelope: authoritativeCopyDefault |
The default value assigned to a document once Authoritative Copy has been enabled at the envelope level. |
Document: authoritativeCopy |
Used to flag individual documents for Authoritative Copy. |
In this chart, you can see how the Authoritative Copy system should behave when combining these parameters.
Envelope creation:
The following JSON body is an example of how to construct an envelope flagged for Authoritative Copy. Note that the authoritativeCopy
and authoritativeCopyDefault
flags are all at the top level of the JSON payload. The third flag, authoritativeCopy
, which enables you to target specific documents within an envelope for export, is handled at the individual document level. Please note that the Authoritative Copy system will not engage until the envelope has entered a completed
status.
{
"authoritativeCopy":"true",
"authoritativeCopyDefault":"true",
"documents": [
{
"authoritativeCopy":"true",
"documentBase64": "JVBER...jMxMzA2MgolJUVPRg==",
"documentId": "1",
"fileExtension": "pdf",
"name": "BaseDocument.pdf",
"order": "1"
},
{
"authoritativeCopy":"true",
"documentBase64": "JVBER...jMxMzA2MgolJUVPRg==",
"documentId": "2",
"fileExtension": "pdf",
"name": "BaseDocument2.pdf",
"order": "2"
}
],
"emailBlurb": "AuthoritativeCopy Test",
"emailSubject": "AuthoritativeCopy Test",
"recipients": {
"signers": [
{
"email": "user@example.com",
"name": "Matt K",
"recipientId": 1,
"routingOrder": "1",
"tabs": {
"signHereTabs": [
{
"pageNumber": "1",
"recipientId": "1",
"documentId":"1",
"tabLabel": "Example Tab",
"xPosition": 0,
"yPosition": 0
}
]
}
}
]
},
"status": "sent"
}
Now that you have the envelope created, let’s take a moment to discuss options for managing these envelopes. You may want to check the current Authoritative Copy status, or update an individual document you did, or did not, intend to flag as authoritative. Docusign’s eSignature REST API does have options to check and update unlocked envelope documents while they are currently in flight.
Checking authoritative copy status on the fly
The individual document flags are the only method presently available to verify documents have been flagged for Authoritative Copy. Note that in the general envelope details and metadata, this parameter is not included.
Action:
GET ../v2.1/accounts/{accountId}/envelopes/{envelopeId}/documents
{
"envelopeId": "20f96145-xxxx-xxxx-xxxx-c9b42fa9774d",
"envelopeDocuments": [
{
"documentId": "1",
"documentIdGuid": "d2267a23-xxxx-xxxx-xxxx-64bc4d594a70",
"name": "BaseDocument.pdf",
"type": "content",
"uri": "/envelopes/20f96145-xxxx-xxxx-xxxx-c9b42fa9774d/documents/1",
"order": "1",
"pages": " ",
"availableDocumentTypes": "",
"display": "inline",
"includeInDownload": "true",
"signerMustAcknowledge": "no_interaction",
"templateRequired": "false",
"authoritativeCopy": "false"
},
{
"documentId": "2",
"documentIdGuid": "f6bf6d5c-xxxx-xxxx-xxxx-b11c72e6402d",
"name": "BaseDocument2.pdf",
"type": "content",
"uri": "/envelopes/20f96145-xxxx-xxxx-xxxx-c9b42fa9774d/documents/2",
"order": "2",
"pages": " ",
"availableDocumentTypes": "",
"display": "inline",
"includeInDownload": "true",
"signerMustAcknowledge": "no_interaction",
"templateRequired": "false",
"authoritativeCopy": "true"
}
]
}
You can see in this example that one of the documents has been flagged for export, but not both. In theory, let’s say that I did not intend to flag it this way and I actually wanted both documents to be vaulted. Now what can I do?
If the envelope has already been locked or completed, unfortunately there isn’t much that you can do. However, the authoritativeCopy
flag can still be toggled as long as the envelope is in either a created
or sent
status. All other statuses will return an error message.
Update Authoritative Copy status on the fly
Action:
PUT //accountId/envelopes/{envelopeId}/documents
{
"documents": [
{
"name": "DocName.pdf",
"order": 1,
"display": "inline",
"documentId": "1",
"authoritativeCopy": true
}
]
}
Response:
{
"envelopeId": "{ENVELOPEID}",
"envelopeDocuments": [
{
"documentId": "1",
"documentIdGuid": "203c9db5-xxxx-xxxx-xxxx-868de3db3d9f",
"name": "DocName.pdf",
"uri": "/envelopes/{ENVELOPEID}/documents/1",
"order": "1",
"templateRequired": "false",
"authoritativeCopy": "true"
}
]
}
Exporting an authoritative copy
How you export an authoritative copy depends on whether you’re using the eOriginal connector or an API call.
If using the eOriginal connector
The eOriginal connector is a premium connector that facilitates the authoritative copy export process and allows for envelopes to be flagged individually. The eOriginal Connector is an all-or-nothing export, similar to our older SOAP models, and operates in the following way:
Add an envelope custom field called “Vault with eOriginal” to your envelope.
- The field must be a list field with values “Yes;No”
- When an envelope is processed through Docusign Connect, the eOriginal Connector checks for the existence of this field. If the value is set to “no”, no vaulting is attempted. If set to “yes”, the envelope is automatically vaulted.
For more information on this Connector and setup, see the Support article How to set up eOriginal vaulting as optional on envelopes.
If using the Docusign APIs
All authoritative copy exports done without the eOriginal Connector use a combination of the same three API calls. Please note that all authoritative export calls must be authenticated and placed through our eSignature SOAP API. Presently there is no direct export option available in the REST APIs.
Below you can see an outline of the required headers, call body, and endpoint to use.
Required parameters: | Description |
---|---|
ApplicationPassword |
The Application Password for the authenticated user. An Application Password can be generated by visiting the user’s profile with Docusign.com and navigating to the Privacy and Security section. |
Email |
The email address of the user you’re authenticating as. |
IntegratorKey |
GUID value of the integration key your system is using to authenticate. |
Timestamp |
Security tokens require the created and expired timestamp in ZULU |
Action:
POST https://{baseUrl}.docusign.net/api/3.0/api.asmx OR https://{baseUrl}.docusign.net/api/3.0/dsapi.asmx
Note: Your integration may already be using one of our SOAP endpoints. Docusign offers two primary SOAP endpoints that handle (mostly) the same SOAP actions: api.asmx and dsapi.asmx.
There are two primary differences between the api.asmx and dsapi.asmx endpoints: api.asmx uses the WSS UserName token supplied within the API call definitions, the dsapi.asmx endpoint uses the X-Docusign-Authentication header. For availability of information, I’ll supply both below.
Be sure to also note the transactionId
and PDFBytes
returned, as they will be required for the next step.
Headers:
Key | Value |
---|---|
Accept |
application/xml |
Content-Type |
text/xml; charset=utf-8 |
SOAPAction |
http://demo.docusign.net/API/3.0/ExportAuthoritativeCopy |
X-Docusign-Authentication |
<DocuSignCredentials><Username>{EMAIL}</Username><Password>{APPLICATIONPASSWORD}</Password><IntegratorKey>{INTEGRATORKEY}</IntegratorKey></DocuSignCredentials> |
Example body:
<?xml version='1.0' encoding='utf-8'?>
<s:Envelope
xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<o:Security s:mustUnderstand="1"
xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<u:Timestamp u:Id="_0">
<u:Created>2024-01-17T16:36:53Z</u:Created>
<u:Expires>2024-01-17T18:36:53Z</u:Expires>
</u:Timestamp>
<o:UsernameToken u:Id="uuid-7ee6ff31-bc85-41fb-8eac-1a64972c8cbc-54">
<o:Username>[{INTEGRATORKEY}]{EMAIL}</o:Username>
<o:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">{APPLICATION PASSWORD}</o:Password>
</o:UsernameToken>
</o:Security>
</s:Header>
<s:Body>
<ExportAuthoritativeCopy
xmlns='http://www.docusign.net/API/3.0'>
<EnvelopeId>{ENVELOPEID}</EnvelopeId>
</ExportAuthoritativeCopy>
</s:Body>
</s:Envelope>
Successful response:
<?xml version="1.0" encoding="utf-8"?>
… …
<soap:Body>
<ExportAuthoritativeCopyResponse
xmlns="http://www.docusign.net/API/3.0">
<ExportAuthoritativeCopyResult>
<EnvelopeId>{ENVELOPEID}</EnvelopeId>
<TransactionId>70825e90-xxxx-xxxx-xxxx-5fe0bb6456e5</TransactionId>
<Count>2</Count>
<DocumentPDF>
<Name>BaseDocument.pdf</Name>
<PDFBytes>xFEimX0iyJM9VytxCBzild681r6...C9YvyaGJhdDw8JiBw==</PDFBytes>
</DocumentPDF>
<DocumentPDF>
<Name>Summary</Name>
<PDFBytes>thq/jN0b9wxl+vxnL2gU3cWldfv...XsX4MROGzY1FeLF3/xR</PDFBytes>
</DocumentPDF>
</ExportAuthoritativeCopyResult>
</ExportAuthoritativeCopyResponse>
</soap:Body>
Now that you have your transaction IDs, you can move on to the next step, which is to compute a checksum of the returned documents. Note that the returned documents are in an archive format and are initially encrypted. The Authoritative Copy process calls to assemble the PDFBytes
are sent back into a byte array and used to create a checksum. Once you have this, you send the TransactionId
and Checksum back to Docusign to acknowledge the authoritative copy export.
Computing the checksum
The portion related to checksum computation can be tricky. I’ll outline the process and take you through it step by step. Note that the checksum uses standard SHA1 cryptography to hash the contents of the previous API response.
- Taking from the response in from the previous step, you should now have your
PDFbytes
, which are Base64-encoded, and atransactionId
. - Take both
PDFbyte
strings, convert them into a byte array, then append them in order.- For example, here we have a document and a Certificate of Completion. If the converted values for the document are ‘abc’ and the converted values for the Certificate of Completion are ‘def’, the string that you apply your hash to is going to be ‘abcdef.’
- Once you have the hash, you convert it back into a Base64 string and add it to your API call. An example of the final hash would be: VSxEWfUxnng+hi6CZpeZkSUEmT4=.
Thanks and credit to my colleague, Renan Araujo, for supplying a Node script for accomplishing this quite easily.
var CryptoJS = require("crypto-js");
var document1 = "<documentPDfBytes>";
var coc = "<CertificatePDFBytes>";
// CryptoJS
var parseB64 = CryptoJS.enc.Base64.parse(document1);
parseB64.concat(CryptoJS.enc.Base64.parse(coc));
var shaB64 = CryptoJS.SHA1(parseB64);
console.log("->", shaB64.toString(CryptoJS.enc.Base64));
Headers:
Key | Value |
---|---|
Accept |
application/xml |
Content-Type |
text/xml; charset=utf-8 |
SOAPAction |
http://demo.docusign.net/API/3.0/AcknowledgeAuthoritativeCopyExport |
X-Docusign-Authentication |
<DocuSignCredentials><Username>{EMAIL}</Username><Password>{APPLICATIONPASSWORD}</Password><IntegratorKey>{INTEGRATORKEY}</IntegratorKey></DocuSignCredentials> |
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope
xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<o:Security s:mustUnderstand="1"
xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<u:Timestamp u:Id="_0">
<u:Created>2024-01-17T16:36:53Z</u:Created>
<u:Expires>2024-01-17T18:36:53Z</u:Expires>
</u:Timestamp>
<o:UsernameToken u:Id="uuid-7ee6ff31-bc85-41fb-8eac-1a64972c8cbc-54">
<o:Username>[{INTEGRATORKEY}]{EMAIL}</o:Username>
<o:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">{APPLICATION PASSWORD}</o:Password>
</o:UsernameToken>
</o:Security>
</s:Header>
<s:Body>
<AcknowledgeAuthoritativeCopyExport
xmlns="http://www.docusign.net/API/3.0">
<EnvelopeId>01026f8b-5573-xxxx-xxxx-4ec1c87bddf5</EnvelopeId>
<TransactionId>f24ebb1f-603b-xxxx-xxxx-ac6d6491294f</TransactionId>
<checkSumHash>VSxEWfUxnng+hi6CZpeZkSUEmT4=</checkSumHash>
</AcknowledgeAuthoritativeCopyExport>
</s:Body>
</s:Envelope>
Successful response:
<?xml version="1.0" encoding="utf-8"?>
… …
<soap:Body>
<AcknowledgeAuthoritativeCopyExportResponse
xmlns="http://www.docusign.net/API/3.0">
<AcknowledgeAuthoritativeCopyExportResult>
<AuthoritativeCopyExportSuccess>true</AuthoritativeCopyExportSuccess>
<EnvelopeId>01026f8b-5573-xxxx-xxxx-4ec1c87bddf5</EnvelopeId>
<ExportKey>faa3ad7d7d744ee0
<ExportKey>
<AcknowledgeAuthoritativeCopyExportResult/>
</soap:Body>
… …
Decrypt the document archive
Finally, you have your ExportKey
. The last step is to decrypt the document archive. Docusign encrypts the document archive using a Rijndael encryption method based on a private key with the parameters listed below. You can decrypt the archive using a dedicated Rijndael or AES-128 library, making sure your decryption call has:
- Mode as CBC
- Padding as PKCS7
- Key size as 128 bits
Block size as 128 bits (this size is fixed for the AES method)
// Node.js
var exportKey = "13ff00ec18fcaad1"; // <-- key returned from AcknowledgeAuthoritativeCopyExport
var exportKeyBytes = CryptoJS.enc.Utf8.parse(exportKey);
var ivBytes = exportKeyBytes;
var docDecrypt = CryptoJS.AES.decrypt(doc1, exportKeyBytes, { iv: ivBytes });
var file = fs.createWriteStream("decrypted.txt");
file.write(CryptoJS.enc.Base64.stringify(docDecrypt));
file.close();
Rijndael has a number of dependencies in various languages to help you decrypt these documents. For considerations of space, I cannot show examples in multiple languages in this guide. Now that you have everything decrypted, you should have access to your exported copy.
Notes:
- During this process, you may encounter watermarks. One will be present in the Demo environment by default, another one will be related to authoritative copy status. Depending on the status of the document, you may see “Authoritative”, or “Copy” watermarked on the envelope. This is to be expected. As for the demo watermark, it will be removed automatically when you move your account into your production environment.
- The individual user permission, ‘Can Export Authoritative Documents,’ for exporting authoritative copies still needs to be enabled at the user level.
- The account level permission, ‘Auto Export Authoritative Copy,’ is meant to automatically flag envelopes and envelope documents for Authoritative Copy by default.
- After acknowledging the Authoritative copy, if the transaction ID, envelope ID, and checksum all agree with the actual envelope and documents, a decryption key is sent back. At this point, the envelope can no longer be exported from the Docusign platform: any attempts to download it will result in an envelope with a “Copy” watermark displayed. Be sure to include robust error handling during the process that logs decryption information until it’s successfully processed. If a document reaches this state, Docusign Support cannot help you recover the document, as we no longer are the Authoritative source for the agreement.