The Black Friday Deal for 2023 is live now. Get Flat 30% OFF on all plans!Learn More

Black Friday Deal - Flat 30% OFF! Learn More

custom-email-notifications-using-aws-ses-and-the-boldsign-api-banner-image

Custom Email Notifications Using AWS SES and the BoldSign API

In this blog, we will explore how to send custom email notifications with the BoldSign API using Amazon Web Services (AWS) and ASP.NET Core. We will go through step-by-step instructions and look at code snippets that demonstrate how to use AWS SES (Simple Email Service) to send signature requests with signing order to signers.

Prerequisites:

  • Basic knowledge of ASP.NET Core and C# programming.
  • An AWS account with SES enabled and access credentials (Access Key ID and Secret Access Key).
  • Visual Studio or Visual Studio Code with the ASP.NET Core SDK installed.
  • If you want to follow along, you’ll need a BoldSign account and an API key. The API key is located on the API Key – BoldSign page. Refer to the BoldSign API documentation for how to generate an API key from your BoldSign account.

Setting up a .NET app

We are now ready to create a demo application. For our demo, we will be creating an ASP.NET Core MVC application and incorporating the BoldSign services into it. However, you can choose your preferred programming language and framework.

Create an ASP.NET Core MVC web app using the following command.

    
dotnet new webapp --framework "net6.0" -o AWSMailMerge
    

Install the BoldSign API, AWS simple email, and the MimeKit NuGet packages with the following command.

    
dotnet add package BoldSign.Api
dotnet add package AWSSDK.SimpleEmail
dotnet add package MimeKit
    

Add BoldSign services through dependency injection

To communicate with the BoldSign API, you need to add authentication details such as headers and a base path to the HttpClient.

Include the following code in the ConfigureServices() method of your startup.cs file.
    
var apiClient = new ApiClient("https://api.boldsign.com/"," ***API Key***");
services.AddSingleton(apiClient);
services.AddSingleton(new DocumentClient(apiClient));
    

Now you can start using the BoldSign APIs in your app.

Step 1: Utilize a webhook to actively monitor and capture the sent event after sending a document for signing.

You can use the following code snippet within your ASP.NET Core application to initiate signature requests for documents, targeting one or multiple recipients. This functionality has various features, including configuring the signing workflow, enabling signing order, and disabling email notifications, among other capabilities. Please refer to the following code example.

    
// The document that needs to be sent for signing.
string filePath = @"Data/Sample.pdf";

List<FormField> firstSignerFormFields = new List<FormField>
{
    new FormField(
        name: "Sign",
        type: FieldType.Signature,
        pageNumber: 1,
        isRequired: true,
        bounds: new Rectangle(x: 50, y: 50, width: 150, height: 32)),
};

List<FormField> secondSignerFormFields = new List<FormField>
{
    new FormField(
        name: "Sign",
        type: FieldType.Signature,
        pageNumber: 2,
        isRequired: true,
        bounds: new Rectangle(x: 100, y: 100, width: 150, height: 32)),
};

var documentDetails = new SendForSign
{
    Title = "Sent from API SDK",
    Message = "This is document message sent from API SDK",
    EnableSigningOrder = true,
    Signers = new List<DocumentSigner>
    {
        new DocumentSigner(
            name: "Signer Name 1",
            emailAddress: "signer1@cubeflakes.com",
            signerOrder: 1,
            signerType: SignerType.Signer,
            privateMessage: "This is private message for signer",
            formFields: firstSignerFormFields),
        new DocumentSigner(
            name: "Signer Name 2",
            emailAddress: "signer2@cubeflakes.com",
            signerOrder: 2,
            signerType: SignerType.Signer,
            privateMessage: "This is private message for signer",
            formFields: secondSignerFormFields),
    },
    Files = new List<IDocumentFile>
    {
        new DocumentFilePath
        {
            ContentType = "application/pdf",

            // directly provide file path.
            FilePath = filePath,
        },
    },
    DisableEmails = true,
};

var documentCreated = await documentClient.SendDocumentAsync(documentDetails);
    

Note: For more details, refer to the send document for signing using BoldSign documentation.

Implement a webhook handler to receive document send requests from your eSignature service. Since document signing is an asynchronous process, we need to listen to the webhook sent event. Check out the webhooks documentation for more details.

Fetch the necessary information (e.g., document ID, signers’ details) from the webhook payload.

    
[HttpPost] 
public async Task<IActionResult> Webhook()
{
    var sr = new StreamReader(this.Request.Body);
    var json = await sr.ReadToEndAsync();

    if (this.Request.Headers[WebhookUtility.BoldSignEventHeader] == "Verification")
    {
        return this.Ok();
    }

    // TODO: Update your webhook secret key.
    var SECRET_KEY = "<<<<SECRET_KEY>>>>";
    
    try
    {
        WebhookUtility.ValidateSignature(json, this.Request.Headers[WebhookUtility.BoldSignSignatureHeader],
            SECRET_KEY);
    }
    catch (BoldSignSignatureException ex)
    {
        Console.WriteLine(ex);

        return this.Forbid();
    }

    var eventPayload = WebhookUtility.ParseEvent(json);
    var doc = eventPayload.Data as DocumentEvent;
    if (doc.DisableEmails == true)
    {
        if (eventPayload.Event.EventType == WebHookEventType.Sent)
        {
            await SendForSign(doc);
        }
    }

    return this.Ok();
}
    
Step 2: Send signature request email for first signer.

Implement an AWS SES service wrapper to handle email sending in your ASP.NET Core project. When a new document send request is received, use AWS SES to send a sign request email to the first signer. Include the signing link, signer details, and instructions in the email content. Utilize the subsequent code snippet to generate the embedded signing link, which will be incorporated into the email content.

    
private async Task SendForSign(DocumentEvent docDetails)
{
    List<SignerDetails> signerDetails = GetSequentialNotCompletedSigners(docDetails.SignerDetails);
    string signerList = GenerateSignerList(signerDetails);

    foreach (var signer in signerDetails)
    {
        string signerEmail = signer.SignerEmail;
        string signerName = signer.SignerName;
        string subject = "Signature Request from {signerName}";
        subject = subject.Replace("{signerName}", signerName);
        string filePath = "EmailTemplates/SendForSign.txt"; // Update the path to your file

       EmbeddedSigningLink signingLink = await documentClient
            .GetEmbeddedSignLinkAsync(docDetails.DocumentId, signer.SignerEmail).ConfigureAwait(false);

        // Customize your email body content here.
        string body = GetEmailBodyContent(signerName, signerList, signingLink.SignLink, filePath);

        // Call the method to send the email.
        await SendEmailAsync(signerEmail, subject, body);
    }
}

private string GenerateSignerList(List<SignerDetails> signerDetails)
{
    StringBuilder sb = new StringBuilder();
    foreach (var signerDetail in signerDetails)
    {
        sb.AppendLine($"<li>{signerDetail.SignerName} ({signerDetail.SignerEmail})</li>");
    }

    return sb.ToString();
}

private List<SignerDetails> GetSequentialNotCompletedSigners(List<SignerDetails> signerDetails)
{
    List<SignerDetails> sequentialNotCompletedSigners = new List<SignerDetails>();
    int expectedOrder = 0;

    foreach (var signer in signerDetails)
    {
        if (signer.Status == SignerStatus.NotCompleted && (signer.Order == expectedOrder || expectedOrder == 0))
        {
            sequentialNotCompletedSigners.Add(signer);
            expectedOrder = signer.Order ?? expectedOrder;
        }
    }

    return sequentialNotCompletedSigners;
}

private async Task SendEmailAsync(string recipientEmail, string subject, string body, byte[] attachment = null)
{
    AWSCredentials credentials = new BasicAWSCredentials("<Your AWS AccessKey>", "<Your AWS SecretKey>");

    using (var client = new AmazonSimpleEmailServiceClient(credentials, RegionEndpoint.USEast1)) // Replace RegionEndpoint with your desired AWS region
    {
        try
        {
            var message = new MimeMessage();
            message.To.Add(new MailboxAddress(null, recipientEmail));
            message.From.Add(new MailboxAddress("Cube flakes", senderEmail));
            message.Subject = subject;

            using (var stream = new MemoryStream())
            {
                var emailBody = ConvertToHtml(body, attachment);
                message.Body = emailBody.ToMessageBody();
                await message.WriteToAsync(stream).ConfigureAwait(false);
                var request = new SendRawEmailRequest
                {
                    RawMessage = new RawMessage(stream),
                };

                var response = await client.SendRawEmailAsync(request).ConfigureAwait(false);
                Console.WriteLine("Email sent! Message ID: " + response.MessageId);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Email sending failed. Error: " + ex.Message);
        }
    }
}

private BodyBuilder ConvertToHtml(string plainText, byte[] attachment = null)
{
    var bodyBuilder = new BodyBuilder();
    bodyBuilder.HtmlBody = $"<p>{plainText}</p>";

    if (attachment != null)
    {
        bodyBuilder.Attachments.Add(documentName, attachment);
    }

    return bodyBuilder;
}

private string GetEmailBodyContent(string signerName, string signerList, string signingLink, string filePath)
{
    string emailTemplate;
    using (var fs = new FileStream(filePath, FileMode.Open))
    using (var sr = new StreamReader(fs))
    {
        emailTemplate = sr.ReadToEnd();
    }

    emailTemplate = emailTemplate.Replace("[SignerName]", signerName);
    emailTemplate = emailTemplate.Replace("[DocumentList]", documentName);

    if (!string.IsNullOrEmpty(signerList))
    {
        emailTemplate = emailTemplate.Replace("[SignerList]", signerList);
    }

    if (!string.IsNullOrEmpty(signingLink))
    {
        emailTemplate = emailTemplate.Replace("[SigningLink]", signingLink);
    }

    return emailTemplate;
}
    
Note: Store the status of the signature request for tracking purposes.

Example customized email template for a signature request.

    
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Signature Request</title>
     <style>
          <<<Your css styles>>>
     </style>
 </head>
 <body>
     <div class="container">
         <div class="header">
             <h1>Signature Request</h1>
         </div>
         <p>Dear [SignerName],</p>
         <p>You are kindly requested to sign the [DocumentList] document.</p>
         <p>The following signers need to sign this document:</p>
         <ul>
            [SignerList]
         </ul>
         <p>Please click the button below to proceed with the signing process:</p>
         <a class="button" href="[SigningLink]">Click here to sign</a>
         <p>Thank you for your cooperation.</p>
         <div class="footer">
             <p>Best regards,<br> Cubeflakes Team </p>
         </div>
     </div>
 </body>
 </html>
    
Step 3: Enable signing order and notify second signer.

When the first signer completes their signing process (based on the webhook or callback), fetch the details of the second signer. Use AWS SES to send a signature request email to the second signer with the appropriate signing link and instructions.

    
var eventPayload = WebhookUtility.ParseEvent(json);
var doc = eventPayload.Data as DocumentEvent;
if (doc.DisableEmails == true)
{
    if (eventPayload.Event.EventType == WebHookEventType.Signed && doc.Status == DocumentStatus.InProgress)
    {
        await SendForSign(doc);
    }
}
    
Step 4: Send completion email with final document.

Implement logic to monitor the completion status of each signer for a document.

    
var eventPayload = WebhookUtility.ParseEvent(json);
var doc = eventPayload.Data as DocumentEvent;
if (doc.DisableEmails == true)
{
    if (eventPayload.Event.EventType == WebHookEventType.Completed && doc.Status == DocumentStatus.Completed)
    {
        await SendForCompleted(doc);
    }
}
    

When all signers complete the signing process, generate the final signed document using the following code snippet. Store the completed document securely and attach it to the completion email. Send the completion email to all signers.

    
private async Task SendForCompleted(DocumentEvent docDetails)
{
    var downloadDocument = await documentClient.DownloadDocumentAsync(docDetails.DocumentId).ConfigureAwait(false);
    byte[] attachment = null;
    using (MemoryStream ms = new MemoryStream())
    {
        downloadDocument.CopyTo(ms);
        attachment = ms.ToArray();
    }

    foreach (var signer in docDetails.SignerDetails)
    {
        string signerEmail = signer.SignerEmail;
        string signerName = signer.SignerName;
        string subject = "Everyone has signed the document";
        string filePath = "EmailTemplates/SendForComplete.txt"; // Update the path to your file

        // Customize your email body content here.
        string body = GetEmailBodyContent(signerName, string.Empty, string.Empty, filePath);

        // Call the method to send the email.
        await SendEmailAsync(signerEmail, subject, body, attachment);
    }
}
    

Handle additional actions like decline, revoke, or reassign, and send corresponding emails using AWS SES.

Conclusion

By following this step-by-step guide, you can implement a robust eSignature workflow using AWS SES and ASP.NET Core. The integration of AWS SES ensures that signers receive timely and personalized email notifications, making the eSignature process more user-friendly and efficient.

We value your feedback. Please take a moment to share your comments below. If you require any assistance or wish to explore our services in more detail, we invite you to schedule a demo or reach out to our dedicated support team via our support portal.
Ramesh Thangaraj

Ramesh Thangaraj

Ramesh is a passionate Product Manager at BoldSign with 6+ years of hands-on experience in software development. He excels in web technologies, mastering .NET and harnessing Vue.js. His full-stack expertise enables him to deliver innovative solutions. Ramesh thrives in cross-functional teams, driving projects forward with his strategic mindset.

Share this blog

Ramesh Thangaraj

Ramesh Thangaraj

Ramesh is a passionate Product Manager at BoldSign with 6+ years of hands-on experience in software development. He excels in web technologies, mastering .NET and harnessing Vue.js. His full-stack expertise enables him to deliver innovative solutions. Ramesh thrives in cross-functional teams, driving projects forward with his strategic mindset.

Subscribe RSS feed

Leave a Reply

Your email address will not be published. Required fields are marked *