I’m testing document signing via a Docusign template through the Docusign API. Once the document signing is complete, each recipient is supposed to receive a Document completed notification, with the link to view the document. But the Document completion Notification is not sent to each recipient as expected, when signing process is initiated using the DocuSign API. Please direct me to the help link on this.
I also, would like to know how to set up the certificate completion document to be sent to each recipient on completion of signing. Any help on above is appreciated.
Thanks
I am seeing you are setting clientUserId
for all types of signers except Vendor roles. Now setting clientUserId
for the signer tells DocuSign that you are implementing embedded signing in your app. As its an embedded signing, so by Default and Design, DocuSign does not send any types of email notification to the embedded signer. To make DocuSign send the completion email with Certification of Completion, you need to modify Signing Settings in your DocuSign Account. In your DS Account, Go To Admin
-> Select Signing Settings
from the Left Hand Side Navigation under SIGNING AND SENDING
section. Then, scroll to the bottom on Signing Settings page to Envelope Delivery
section as shown in below screenshot, select Send Completion emails to embedded signers and also select Attach Certificate of completion to envelope. This should resolve your issue.
Please find below the code for creating and sending envelope and initiating signing for the Agent and Purchaser roles.
[Authorize]
[HttpPost]
[ValidateAntiForgeryToken]
public JsonResult Create(CreateContractViewModel viewModel)
{
if (!ModelState.IsValid)
return Json(new { success = false, errors=ModelStateExtensions.GetErrors(ViewData.ModelState.Values) }, JsonRequestBehavior.AllowGet);
try
{
#region "Validation"
if (viewModel.Purchasers == null) //only allow max. of 2 purchasers at this point in time.
return Json(new { success = false, errors = "Minimum of one Purchaser is required." },
JsonRequestBehavior.AllowGet);
if (viewModel.Purchasers.Count > 2) //only allow max. of 2 purchasers at this point in time.
return Json(new { success = false, errors = "Number of Purchasers allowed exceed." },
JsonRequestBehavior.AllowGet);
#endregion
// Get template based on drop down selection.
var envTemplate = Templates.Get(viewModel.SelectTemplateId);
// Assign all parties. eg. recipients, signers, and CCs
var docusignRecipients = envTemplate.Recipients;
var signers = docusignRecipients.Signers;
var carbonCopies = docusignRecipients.CarbonCopies;
var templateRolesList = new List<TemplateRole>();
if (viewModel.Purchasers.Count == 1) //remove Purchaser 2 if there's only 1 Purchaser needed.
{
var remove = signers.SingleOrDefault(x => x.RoleName.Equals(RoleNames.Purchaser_2));
if (remove != null)
signers.Remove(remove);
}
// Minimum of 1 Purchaser must exist.
var purchaser1 = viewModel.Purchasers[0];
var purchaser2 = viewModel.Purchasers.Count > 1
? viewModel.Purchasers[1]
: new Purchaser();
#region "Setup Template Roles"
// Recipients
var envelopeRecipients = new Recipients
{
RecipientCount = docusignRecipients.RecipientCount,
Signers = new List<Signer>(),
CarbonCopies = new List<CarbonCopy>()
};
// Signers
TemplateRoles.Signers.AddRoles(viewModel, signers, purchaser1, purchaser2, envelopeRecipients,
docusignRecipients);
// Carbon Copies
TemplateRoles.CarbonCopies.AddRoles(carbonCopies, envelopeRecipients, docusignRecipients);
#endregion
#region "Prepare & Create Envelope"
// Server template
var serverTemplates = new List<ServerTemplate>
{
new ServerTemplate
{
Sequence = "1",
TemplateId = viewModel.SelectTemplateId
}
};
// Server inline template
var serverInlineTemplates = new List<InlineTemplate>
{
new InlineTemplate
{
Sequence = "2",
Recipients = envelopeRecipients
}
};
// refactor this part to a new method.
var documentFactory = new DocumentFactory(viewModel.SelectTemplateId, _documentService);
var type = viewModel.OptionalInserts.GetType();
var optionalInserts = type.GetProperties();
var templateList = new List<CompositeTemplate>();
foreach (var insert in optionalInserts)
{
var value = insert.GetValue(viewModel.OptionalInserts);
var isChecked = (bool)value;
if (!isChecked) continue;
var template = documentFactory.GetTemplate(insert.Name);
var compositeTemplate = template.Compose();
templateList.Add(compositeTemplate);
}
// Prepare composite template, combining the Server and Inline templates
var compositeTemplates = new List<CompositeTemplate>
{
new CompositeTemplate
{
ServerTemplates = serverTemplates,
InlineTemplates = serverInlineTemplates
}
};
templateList.ForEach(t => compositeTemplates.Add(t));
// Create Envelope Definition
var envDef = Envelopes.CreateEnvDef(envTemplate, compositeTemplates);
// Create Envelope
var envSummary = Envelopes.CreateEnvelope(envDef);
#endregion
#region "Generate View Url"
// Generate recipient Url - Agent as 1st recipient by default
var agent = envelopeRecipients.Signers.SingleOrDefault(x => x.RoleName.Equals(RoleNames.Agent));
if (agent == null)
return
Json(
new
{
success = false,
errors = "No Agent role has been found.",
JsonRequestBehavior.AllowGet
});
var returnUrl = Url.Action("Sign", "Contract",
new { envelopeId = envSummary.EnvelopeId, routingOrder = agent.RoutingOrder, selectedTemplateId = viewModel.SelectTemplateId });//modified on 15-6-2017
// Get View Options.
var viewOptions = DocuSign.Recipients.ViewRequest(returnUrl, agent.ClientUserId, agent.Name, agent.Email);
// Create Recipient View
var view = Envelopes.CreateRecipientView(envSummary.EnvelopeId, viewOptions);
#endregion
return Json(new { success = true, returnUrl = view.Url, JsonRequestBehavior.AllowGet });
}
catch (Exception e)
{
return Json(new
{
success = false,
errors = e.Message,
JsonRequestBehavior.AllowGet
});
}
}
public ActionResult Sign(CreateContractViewModel viewModel)
//public ActionResult Sign() //previous code
{
var paramEvent = Request.QueryString["event"];
var paramEnvId = Request.QueryString["envelopeId"];
var paramRoutingOrder = Request.QueryString["routingOrder"];
var selectedTemplateId = Request.QueryString["selectedTemplateId"];
var contractIdQueryString = Request.QueryString["contractId"];
// Validation
if (string.IsNullOrEmpty(paramEvent) || string.IsNullOrEmpty(paramEnvId) ||
string.IsNullOrEmpty(paramRoutingOrder) || (paramEvent != SigningStatus.Signing_Complete))
return View("Error");
// Get next Signer
var recipients = Envelopes.ListRecipients(paramEnvId);
var signers = recipients.Signers;
///////addded on 25/5/2017 to check docusign value extraction
//var cfe = Envelopes.ListRecipientsWithTags(paramEnvId);
//Envelopes.ListCustomFields(paramEnvId);
//List<TextCustomField> tcfList = cfe.TextCustomFields;
//foreach (var tcf in tcfList)
//{
// string dfd = tcf.FieldId;
// string ddfdfd = tcf.Name;
// string sdf = tcf.Value;
// //string str = cfe.TextCustomFields[0].FieldId;
//}
//EnvelopesApi envelopesApi = new EnvelopesApi();
//CustomFieldsEnvelope cfe = envelopesApi.ListCustomFields(accountId, _templateId2);
//TemplateCustomFields cfUpdate = new TemplateCustomFields();
//cfUpdate.TextCustomFields = new System.Collections.Generic.List<TextCustomField>();
//TextCustomField tcf = new TextCustomField();
//tcf.FieldId = cfe.TextCustomFields[0].FieldId;
// Get template based on drop down selection.
//var envTemplate = Templates.Get(viewModel.SelectTemplateId);
//// Assign all parties. eg. recipients, signers, and CCs
//var docusignRecipients = envTemplate.Recipients;
//var signers1 = docusignRecipients.Signers;
int ContractId = 0;
if (contractIdQueryString != null && contractIdQueryString !="")
{
ContractId = int.Parse(contractIdQueryString.Trim());
}
ContractId= GetTabs(signers, selectedTemplateId, paramRoutingOrder, ContractId);
/////
//note:ClientUserId is made null only for the Vendor role in TemplateRoles.Signers.AddRoles method, so that
//signing continues for the Agent and Purchaser roles only
var nextSigner = (from s in signers
where
!string.IsNullOrEmpty(s.ClientUserId) &&
(Convert.ToInt32(s.RoutingOrder) > Convert.ToInt32(paramRoutingOrder))
orderby s.RoutingOrder
select s).FirstOrDefault();
//added following on 06/06/2018 to prevent workflow from proceeding to the next signers. But this will prevent capturing signed dates by signers
//if( paramEvent == "signing_complete" && paramRoutingOrder == "1")
// {
// return View("Completed");
// }
// return View("Completed");
//
#region Code that proceeds workflow to the next signer. Need to have an option to allow agent to go for following approach which allows purchaser to sign via the application or, another option that allows purchaser to receive an email notification for completing signing as done in above commented code
// No next Signer redirect to Completed page.
if (nextSigner == null) return View("Completed");
//var returnUrl = Url.Action("Sign", "Contract",
// new
// {
// envelopeId = paramEnvId,
// routingOrder = nextSigner.RoutingOrder,
// recipientId = nextSigner.RecipientId
// });//original code
var returnUrl = Url.Action("Sign", "Contract",
new
{
envelopeId = paramEnvId,
routingOrder = nextSigner.RoutingOrder,
recipientId = nextSigner.RecipientId,
selectedTemplateId = selectedTemplateId,
contractId = ContractId
});//modified on 19-6-2017
// Get View Options.
var viewOptions = DocuSign.Recipients.ViewRequest(returnUrl, nextSigner.ClientUserId, nextSigner.Name,
nextSigner.Email);
// Create Recipient View
var view = Envelopes.CreateRecipientView(paramEnvId, viewOptions);
return Redirect(view.Url);
#endregion
}
public static class TemplateRoles
{
public static class Signers
{
public static void AddRoles(CreateContractViewModel viewModel, List<Signer> signers, Purchaser purchaser1, Purchaser purchaser2,
global::DocuSign.eSign.Model.Recipients r, global::DocuSign.eSign.Model.Recipients recipients)
{
try
{
foreach (var signer in signers)
{
switch (signer.RoleName)
{
default:
throw new Exception("Unknown Signer role was found on the template.");
region "Role: Agent"
case RoleNames.Agent:
// Fill all Sign tabs for Agent role, which includes Purchaser fields.
// Agent role is the first point of the draft, therefore all the fields need to be prefilled.
var signerTabs = signer.Tabs;
if (signerTabs != null)
{
if (signerTabs.TextTabs != null)
{
var signerTextTabs = signerTabs.TextTabs;
DocuSign.Tabs.Sign(viewModel, signerTextTabs, purchaser1, purchaser2);
}
if (signerTabs.CheckboxTabs != null)
{
var signerCheckboxTabs = signerTabs.CheckboxTabs;
DocuSign.Tabs.SignCheckBoxes(viewModel, signerCheckboxTabs, purchaser1, purchaser2);//Assigning check box values
}
}
var agentSigner = recipients.Signers.Find(x => x.RoleName == "Agent");
if (agentSigner != null)
{
var s = new Signer();
s = agentSigner;
s.RoleName = signer.RoleName;
s.Name = signer.Name;
s.Email = signer.Email;
s.RoutingOrder = signer.RoutingOrder;
s.ClientUserId = Guid.NewGuid().ToString();
s.Tabs = signerTabs;
r.Signers.Add(s);
}
break;
#endregion
#region "Role: Purchaser 1"
case RoleNames.Purchaser_1:
var purchaserSigner = recipients.Signers.Find(x => x.RoleName == "Purchaser 1");
if (purchaserSigner != null)
{
var p1 = new Signer();
p1 = purchaserSigner;
p1.RoleName = RoleNames.Purchaser_1;
p1.Name =
(!string.IsNullOrEmpty(purchaser1.CompanyName)
? purchaser1.CompanyName
: $"{purchaser1.FirstName} {purchaser1.Surname}");
p1.Email = (!string.IsNullOrEmpty(purchaser1.Email) ? purchaser1.Email : null);
p1.RoutingOrder = signer.RoutingOrder;
p1.ClientUserId = Guid.NewGuid().ToString();
p1.Tabs = signer.Tabs;
r.Signers.Add(p1);
}
break;
#endregion
#region "Role: Purchaser 2"
case RoleNames.Purchaser_2:
var purchaser2Signer = recipients.Signers.Find(x => x.RoleName == "Purchaser 2");
if (purchaser2Signer != null)
{
var p2 = new Signer();
p2 = purchaser2Signer;
p2.RoleName = RoleNames.Purchaser_2;
p2.Name =
(!string.IsNullOrEmpty(purchaser2.CompanyName)
? purchaser2.CompanyName
: $"{purchaser2.FirstName} {purchaser2.Surname}");
p2.Email = (!string.IsNullOrEmpty(purchaser2.Email) ? purchaser2.Email : null);
p2.RoutingOrder = signer.RoutingOrder;
p2.ClientUserId = Guid.NewGuid().ToString();
p2.Tabs = signer.Tabs;
r.Signers.Add(p2);
}
break;
#endregion
#region "Role: Vendor"
case RoleNames.Vendor: // No embedded signing.
var vendorSigner = recipients.Signers.Find(x => x.RoleName == "Vendor");
if (vendorSigner != null)
{
var v = new Signer();
v = vendorSigner;
v.RoleName = signer.RoleName;
v.Name = signer.Name;
v.Email = signer.Email;
v.RoutingOrder = signer.RoutingOrder;
v.ClientUserId = null;
r.Signers.Add(v);
}
break;
#endregion
}
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
}
}