
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
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.
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
To communicate with the BoldSign API, you need to add authentication details such as headers and a base path to the HttpClient.
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.
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();
}
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;
}
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>
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);
}
}
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.
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.
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.
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.
Latest Articles
Best Black Friday Software Deals in 2023
Why BoldSign is a better Alternative to DocuSign