How to certify your documents using a Docusign electronic seal via API

In this blog post, I’m going to show how you can apply an electronic seal on the document to certify the origin and authenticity of these documents.

So first, what’s an electronic seal according to eIDAS ? 

An electronic seal is  data in electronic form, which is attached to or logically associated with other data in electronic form to ensure the latter’s origin and integrity, where the creator of a seal is a legal person (unlike the electronic signature that is issued by a natural person). 

And how is it reflected when using Docusign ?

Each electronic seal that is applied to a PDF document is characterized by a digital certificate. Seal certificates do not show in the content of the complete PDF document itself, but in its signature properties. When a user displays such a PDF document, one can notice the presence of the electronic seal by displaying the signature details.

eSignature details reveal the digital certificate

It is also possible to include a visual representation of your electronic seal and place it at the desired location in your document.

Adding a visual representation to the document

For example, a company may apply an electronic seal on a contract before sending it out for signature or might use it to digitally seal their invoices before sending them to their customers to ensure their origin.

In the rest of this blog article, I will focus on a eInvoicing use case where:

  • The invoice has been already generated from a third-party system
  • The invoice is passed to Docusign to certify its origin and will be communicated to the customer using a carbon copy recipient
  • As part of the sealing process, a visual representation of that seal will be added to the document using an anchor tag
  • Additionally, the invoice will be pushed automatically to an eArchiving system using an eventNotification/webhook defined at envelope level

Prerequisites

To proceed, you will need to have: 

  • A Docusign account with the Standards-Based Signature module enabled
  • At least one electronic seal certificate provisioned on this account
  • A user member of a group who has access to this electronic seal
  • Already implemented a Connect Listener (see our Developer Center guide JSON SIM event model (Connect 2.0))

Note: Your account might not support the option discussed in this blog article. If you don’t have such an account, contact Docusign Sales and discuss any possible configuration details.

Step 0: Set up your integration key

If this is your first API integration with Docusign, you will need to create an integration key.

As this use case will be a system integration, it will rely on JWT for the authentication process. You can refer to JSON Web Token (JWT) Grant authentication and its associated links.

Step 1: Identify the Seal Identifier/sealName

To apply the seal to your envelope, you would need to know its Seal Identifier, also known as its sealName in our API terminology.

You can apply the seal in your eSignature account under Settings > Electronic Seals, as shown below:

Electronic Seals section of eSignature settings

If you are looking to automate this process, the AccountSealProviders:list method will return the list of Electronic Seals available for your account. Here is a sample of the returned JSON on the same account from the AccountSealProviders:list call:

{
   "seals": [
       {
           "sealName": "b9a15451-3ef4-4a5d-8d60-xxxxxxxxxxxx",
           "sealDisplayName": "Demo Seal - PaperPreventers"
       },
       {
           "sealName": "dda61191-635d-46cd-8ef2-xxxxxxxxxxxx",
           "sealDisplayName": "Demo Seal - Summit Credit"
       }
   ]
}

Let’s assume you are looking to seal the document using the PaperPreventers Seal certificate, so the identifier you need to use for the rest of this blog is: b9a15451-3ef4-4a5d-8d60-xxxxxxxxxxxx.

Step 2: Create the envelope with the eSeal

Now that you have identified the seal that will be applied, you need to create an envelope using the Envelopes:create endpoint: /restapi/v2.1/accounts/{accountId}/envelopes

To match your invoice use case, you will need to: 

  • Pass the document encoded in Base64 format
  • Make sure to use the following recipient types: 
    • seal with its corresponding recipientSignatureProvider information and tab property for the visual representation placement based on an anchor
    • carboncopy with the contact details of the customer
  • Use an envelope custom field to give some context for the archiving system regarding the transaction with the InvoiceID and the CustomerID
  • Define the webhook listener for JSON notifications in charge of archiving the sealed document when the envelope is in the completed state.

Your resulting envelope definition JSON should look something like this:

{
 "emailSubject": "Your Monthly Invoice from PaperPreventers",
 "status": "sent",
 "documents": [
   {
     "documentBase64": "<place here your document base64 encoded>",
     "documentId": "1",
     "fileExtension": "pdf",
     "name": "invoice.pdf"
   }
 ],
 "recipients": {
   "seals": [
     {
       "recipientId": "1",
       "routingOrder": "1",
       "recipientSignatureProviders": [
         {
           "sealDocumentsWithTabsOnly": "false",
           "sealName": "b9a15451-3ef4-4a5d-8d60-xxxxxxxxxxxx"
         }
       ],
       "tabs": {
         "signHereTabs": [
           {
             "anchorString": "myESealAnchor",
             "recipientId": "1"
           }
         ]
       }
     }
   ],
   "carbonCopies": [
     {
       "recipientId": "99",
       "routingOrder": "99",
       "name": "John Doe",
       "email": "my.customer@mailinator.com"
     }
   ]
 },
 "customFields": {
   "textCustomFields": [
     {
       "name": "Invoice ID",
       "show": "true",
       "required": "false",
       "value": "123456"
     },
     {
       "name": "Customer ID",
       "show": "true",
       "required": "false",
       "value": "JD4182"
     }
   ]
 },
 "eventNotification": {
   "url": "https://webhook.site/a494ce82-6e6e-4130-8904-xxxxxxxxxxxx",
   "requireAcknowledgement": "true",
   "loggingEnabled": "true",
   "deliveryMode": "SIM",
   "events": [
     "envelope-completed"
   ],
   "eventData": {
     "version": "restv2.1",
     "format": "json",
     "includeData": [
       "custom_fields",  "documents"
     ]
   }
 }
}

You should obtain as a response something with the following data structure:

{
   "envelopeId": "a7938bf5-5667-4a2d-9976-xxxxxxxxxxxx",
   "uri": "/envelopes/a7938bf5-5667-4a2d-9976-xxxxxxxxxxxx",
   "statusDateTime": "2023-02-09T17:17:18.3370000Z",
   "status": "sent"
}

Now the envelope has been created and sent.

Within a moment, your customer should receive an email from Docusign showing where they can access the invoice, or even obtain the invoice directly as an attachment if the Attach documents to completion email setting has been set up at the account level.

Step 3: Check the data received by your listener 

In the meantime, the event notification is sent to your listener with a payload similar to the following one:

{
 ...
 "event": "envelope-completed",
 "apiVersion": "v2.1",
 "uri": "/restapi/v2.1/accounts/37a5be9b-b83e-4498-aa68-xxxxxxxxxxxx/envelopes/a7938bf5-5667-4a2d-9976-xxxxxxxxxxxx",
 "retryCount": 0,
 "configurationId": 0,
 "generatedDateTime": "2023-02-10T01:18:05.2710000Z",
 "data": {
   "accountId": "37a5be9b-b83e-4498-aa68-xxxxxxxxxxxx",
   "userId": "e3a65b13-513a-4b63-b715-xxxxxxxxxxxx",
   "envelopeId": "a7938bf5-5667-4a2d-9976-xxxxxxxxxxxx",
   "envelopeSummary": {
     "status": "completed",
     ...
     "emailSubject": "Your Monthly Invoice from PaperPreventers",
     "envelopeId": "a7938bf5-5667-4a2d-9976-xxxxxxxxxxxx",
     ...     
     "createdDateTime": "2023-02-09T17:17:17.273Z",
     "lastModifiedDateTime": "2023-02-09T17:17:17.273Z",
     "deliveredDateTime": "2023-02-09T17:17:45.24Z",
     "initialSentDateTime": "2023-02-09T17:17:18.337Z",
     "sentDateTime": "2023-02-09T17:17:45.24Z",
     "completedDateTime": "2023-02-09T17:17:45.24Z",
	...
     "customFields": {
       "textCustomFields": [
         {
           "fieldId": "10847266193",
           "name": "Invoice ID",
           "show": "true",
           "required": "false",
           "value": "123456"
         },
         {
           "fieldId": "10847266194",
           "name": "Customer ID",
           "show": "true",
           "required": "false",
           "value": "JD4182"
         }
       ],
       "listCustomFields": []
     },
     "envelopeDocuments": [
       {
         "documentId": "1",
         "documentIdGuid": "813b45a7-3c30-4936-8893-xxxxxxxxxxxx",
         "name": "invoice.pdf",
         "type": "content",
         "uri": "/envelopes/a7938bf5-5667-4a2d-9976-xxxxxxxxxxxx/documents/1",
         "order": "1",
         "pages": [
           {
             "pageId": "a0a2eaee-bcbe-4d98-9449-xxxxxxxxxxxx",
             "sequence": "1",
             "height": "842",
             "width": "595",
             "dpi": "72"
           }
         ],
         ...
         "PDFBytes": "<certified document base64 encoded>"
       }
     ],
     ...
   }
 }
}

From here your webhook listener will be able to focus on:

  • The envelope-completed type of events
  • Extract the context using the envelope custom field value positioned under data.envelopeSummary.customFields
  • Extract the sealed document, which is Base64-encoded, from data.envelopeDocuments[0].PDFBytes

in order to archive the document into your archiving system.

Additional resources

Arnaud Lesueur
Author
Arnaud Lesueur
Principal Solutions Architect
Published