I'm working on Embedded signing and trying to create an envelope from a template.
After I create the envelope and create a signing URL(This logic works fine). But when I click the signing URL I see no Tabs in the document to sign. I have to drag and drop the tabs(as a Signer).
The template is simple with One recipient with a role (Without name and email).
public class EmbeddedSigningController {
#AuraEnabled
public static String sendEnvelope(String[] template, String[] description, Id recordId, String memberId, String memberEmail, String memberRId, String memberName) {
try {
List<dfsle.Document> docs = new List<dfsle.Document>();
for(Integer i=0; i < template.size(); i++) {
docs.add(dfsle.Document.fromTemplate(dfsle.UUID.parse(template[i]), description[i]));
}
dfsle.CustomField CustField = new dfsle.CustomField('text', '##SFCase', recordId, null, true, true);
dfsle.Envelope dsEnvelope = dfsle.EnvelopeService.getEmptyEnvelope(
new dfsle.Entity(recordId))
.withDocuments(docs)
.withCustomFields(new List<dfsle.CustomField> { CustField })
.withRecipients(new List<dfsle.Recipient> { dfsle.Recipient.newEmbeddedSigner(memberName, memberEmail, memberRId) }
);
dsEnvelope = dfsle.EnvelopeService.sendEnvelope(dsEnvelope, true );
System.debug('EnvelopeId: ' + String.valueOf(dsEnvelope.docuSignId));
PlatformEventService pes = new PlatformEventService();
Boolean record = pes.createMbrDocusignEnvelopeEvent(recordId, String.valueOf(dsEnvelope.docuSignId), memberId, memberEmail);
if(record == false) {
throw new EmbeddedSigningException('Error Creating Platform Event');
}
return String.valueOf(dsEnvelope.docuSignId);
}
catch (Exception e){
System.debug('Error in catch: ' + e.getMessage());
return e.getMessage();
}
}
#AuraEnabled
public static Boolean voidEnvelope(String envelopeId, String reason) {
Boolean isVoided = dfsle.StatusService.voidEnvelope(dfsle.UUID.parse(envelopeId), reason);
System.debug('isVoided: ' + isVoided);
return isVoided;
}
}
Inbar is correct, you need to specify which role in the template the signer is fulfilling. Also, why are you specifying a document if you're using a template?
See my blog post for a full discussion of using templates. It includes links to live examples you can try out via the API Request Builder.
Here's a quick example of setting a role with C#:
// Create the envelope request and send it to DocuSign
// Returns the resulting envelopeId or ""
static string SendDocuSignEnvelope()
{
TemplateRole templateRole1 = new TemplateRole
{
Email = null,
Name = null,
RoleName = "signer1"
};
TemplateRole templateRole2 = new TemplateRole
{
Email = null,
Name = null,
RoleName = "signer2"
};
List<TemplateRole> templateRoles1 = new List<TemplateRole> {templateRole1, templateRole2};
EnvelopeDefinition envelopeDefinition = new EnvelopeDefinition
{
Status = "sent",
TemplateId = "61973947-7d29-490d-9bac-5fa05846b10e",
TemplateRoles = templateRoles1
};
ApiClient apiClient = new ApiClient(basePath);
apiClient.Configuration.AddDefaultHeader("Authorization", "Bearer " + accessToken);
EnvelopesApi envelopesApi = new EnvelopesApi(apiClient);
try
{
EnvelopeSummary results = envelopesApi.CreateEnvelope(accountId, envelopeDefinition);
Console.WriteLine($"Envelope status: {results.Status}. Envelope ID: {results.EnvelopeId}");
return results.EnvelopeId;
}
catch (ApiException e)
{
Console.WriteLine("Exception while creating envelope!");
Console.WriteLine($"Code: {e.ErrorCode}\nContent: {e.ErrorContent}");
//Console.WriteLine(e.Message);
return "";
}
}
Related
I am trying to implement sign a document option in my application.
It is working perfectly but during the process it sends email to signer to review document (which doesn't happen when created via DocuSign UI). How I can stop this review document email from sending to signer.
Here what I am doing in my code
Creating envelope
try
{
EnvelopeDefinition envDef = new EnvelopeDefinition();
envDef.EmailSubject = selectedFileList[0].subject;
envDef.Documents = new List<Document>();
foreach (var file in selectedFileList)
{
envDef.Documents.Add(getDocument(file.localPath, file.fullFileName, file.extension, file.number.ToString(), file.version.ToString(), file.databaseName));
}
Signer signer = new Signer();
signer.Email = signerEmail;
signer.Name = signerName;
signer.RecipientId = "1";
envDef.Recipients = new Recipients();
envDef.Recipients.Signers = new List<Signer>();
envDef.Recipients.Signers.Add(signer);
envDef.Status = "sent";
EnvelopesApi envelopesApi = new EnvelopesApi(config);
var response = await Task.Run(() => { return envelopesApi.CreateEnvelopeWithHttpInfo(accountId, envDef); });
EnvelopeSummary envelopeSummary = response.Data;
responseHeaders = response.Headers;
}
catch (Exception ex)
{
throw ex;
}
Creating receipent view
EnvelopesApi envelopesApi = new EnvelopesApi(apiConfig);
RecipientViewRequest viewRequest = new RecipientViewRequest();
viewRequest.UserName = signerName;
viewRequest.Email = signerEmail;
viewRequest.RecipientId = "1";
viewRequest.AuthenticationMethod = "email";
viewRequest.ReturnUrl = returnUrl;
//viewRequest.ClientUserId = apiConfig;
var view = envelopesApi.CreateRecipientView(accountId, envelopeId, viewRequest);
var url = view.Url;
Try to add this code in your app:
var textCustomFields = new List<TextCustomField>();
var textCustomField = new TextCustomField { Name = "AppName", Value = "DocuSignIt" };
textCustomFields.Add(textCustomField);
envDef.CustomFields.TextCustomFields = textCustomFields;
This should tell DocuSign not to send the email.
Setting the ClientUserId attribute value for a recipient also marks the recipient to not receive the email invitation to sign.
Setting ClientUserId is the recommended approach for embedded signing.
The value of the ClientUserId attribute also acts as a connection between the authentication that your application has done and the signing ceremony which enables the person to sign the documents.
Using an demo account and my accountID, I am trying to change the Notification settings on an envelope I am creating using the REST API. I get the error below. Is this a settings issue with my account, or a problem with the way I am creating the envelope?
Error calling UpdateNotificationSettings: {
"errorCode": "USER_LACKS_PERMISSIONS",
"message": "This user lacks sufficient permissions to access this resource."
}
Code:
Recipients recipients = new Recipients { Signers = new List<Signer>(signers) };
EnvelopeDefinition envelopeDefinition = new EnvelopeDefinition
{
EmailSubject = "Review and sign the document",
Documents = new List<Document>(documents),
Recipients = recipients,
Status = "sent"
};
ApiClient apiClient = new ApiClient(basePath);
apiClient.Configuration.AddDefaultHeader("Authorization", "Bearer " + accessToken);
EnvelopesApi envelopesApi = new EnvelopesApi(apiClient.Configuration);
EnvelopeSummary results = envelopesApi.CreateEnvelope(accountId, envelopeDefinition);
Expirations exp = new Expirations("14", "true", "2");
EnvelopeNotificationRequest enr = new EnvelopeNotificationRequest(exp, null, null);
string envelopeId = results.EnvelopeId;
envelopesApi.UpdateNotificationSettings(accountId, envelopeId, enr);
It looks like you're sending the envelope and then trying to change the expiration settings while it's live.
You might have better results adding that expirations to the envelope definition prior to the CreateEnvelope() call. Try this instead:
Recipients recipients = new Recipients { Signers = new List<Signer>(signers) };
EnvelopeDefinition envelopeDefinition = new EnvelopeDefinition
{
EmailSubject = "Review and sign the document",
Documents = new List<Document>(documents),
Recipients = recipients,
Status = "sent"
};
Expirations exp = new Expirations("14", "true", "2");
envelopeDefinition.Notification = new Notification(expirations)
ApiClient apiClient = new ApiClient(basePath);
apiClient.Configuration.AddDefaultHeader("Authorization", "Bearer " + accessToken);
EnvelopesApi envelopesApi = new EnvelopesApi(apiClient.Configuration);
EnvelopeSummary results = envelopesApi.CreateEnvelope(accountId, envelopeDefinition);
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);
}
}
}
}
I have create a template in my Admin Account Panel, and I am using the template to create new envelopes and send to different receivers.
But in my template I have a dropdown whose value changes on some condition,
like for State A it will have different values, for State B it will have different values.
How do I handle it programmatically.
Here is how I create an envelope from a template.
string recipientEmail = "a#a.com";
string recipientName = "John Doe";
string templateRoleName = "Customer";
string TemplateId = "xxxxxxxx-c87454e95429";
EnvelopeDefinition envDef = new EnvelopeDefinition();
envDef.EmailSubject = "[DocuSign C# SDK] - Please sign this doc";
// assign recipient to template role by setting name, email, and role name. Note that the
// template role name must match the placeholder role name saved in your account template.
TemplateRole tRole = new TemplateRole();
tRole.Email = recipientEmail;
tRole.Name = recipientName;
tRole.RoleName = templateRoleName;
List<TemplateRole> rolesList = new List<TemplateRole>() { tRole };
// add the role to the envelope and assign valid templateId from your account
envDef.TemplateRoles = rolesList;
envDef.TemplateId = TemplateId;
// set envelope status to "sent" to immediately send the signature request
envDef.Status = "sent";
// |EnvelopesApi| contains methods related to creating and sending Envelopes (aka signature requests)
EnvelopesApi envelopesApi = new EnvelopesApi(cfi);
EnvelopeSummary envelopeSummary = envelopesApi.CreateEnvelope(accountID, envDef);
To populate tabs in a template you must match the name of the tab using the tabLabel property and set its value to the data you want to populate it with
Documentation here
string recipientEmail = "a#a.com";
string recipientName = "John Doe";
string templateRoleName = "Customer";
string TemplateId = "xxxxxxxx-c87454e95429";
EnvelopeDefinition envDef = new EnvelopeDefinition();
envDef.EmailSubject = "[DocuSign C# SDK] - Please sign this doc";
// assign recipient to template role by setting name, email, and role name. Note that the
// template role name must match the placeholder role name saved in your account template.
var tRole = new TemplateRole();
tRole.Email = recipientEmail;
tRole.Name = recipientName;
tRole.RoleName = templateRoleName;
var dropdownItems = new List<ListItem>();
if (stateA)
{
dropdownItems.Add(new ListItem()
{
Text = "Yellow", Value = "Y", Selected = "true"
});
dropdownItems.Add(new ListItem()
{
Text = "Green",Value = "G"
});
}
else
{
dropdownItems.Add(new ListItem()
{
Text = "Red", Value = "R", Selected = "true"
});
dropdownItems.Add(new ListItem()
{
Text = "Blue", Value = "B"
});
dropdownItems.Add(new ListItem()
{
Text = "Orange", Value = "O"
});
}
tRole.Tabs = new Tabs()
{
ListTabs = new List<List>()
{
new List(){
TabLabel = "ColorDropdown",
ListItems = dropdownItems
}
}
};
var rolesList = new List<TemplateRole>() { tRole };
// add the role to the envelope and assign valid templateId from your account
envDef.TemplateRoles = rolesList;
envDef.TemplateId = TemplateId;
// set envelope status to "sent" to immediately send the signature request
envDef.Status = "sent";
// |EnvelopesApi| contains methods related to creating and sending Envelopes (aka signature requests)
EnvelopesApi envelopesApi = new EnvelopesApi();
EnvelopeSummary envelopeSummary = envelopesApi.CreateEnvelope(accountID, envDef);
We are trying to use the O365 Unified API to send emails from our line-of-business apps. I use the following code to send the email. This throws a DataServiceQueryException exception "Unauthorized".
public async Task SendEmailAsUserAsync(EmailMessage message)
{
try
{
var graphClient = await _authenticationHelper.GetGraphClientAsync();
Message m = InitializeMessage(message);
await graphClient.Me.SendMailAsync(m, true);
}
catch (DataServiceQueryException dsqe)
{
_logger.Error("Could not get files: " + dsqe.InnerException.Message, dsqe);
throw;
}
}
private static Message InitializeMessage(EmailMessage message)
{
ItemBody body = new ItemBody {Content = message.Body, ContentType = BodyType.HTML};
Message m = new Message
{
Body = body,
Subject = message.Subject,
Importance = Importance.Normal,
};
//Add all the to email ids
if (message.ToRecipients != null)
foreach (Models.Messaging.EmailAddress emailAddress in message.ToRecipients)
{
m.ToRecipients.Add(new Recipient { EmailAddress = new Microsoft.Graph.EmailAddress { Address = emailAddress.Address, Name = emailAddress.Name } });
}
return m;
}
The code for _authenticationHelper.GetGraphClientAsync() is
public async Task<GraphService> GetGraphClientAsync()
{
Uri serviceRoot = new Uri(appConfig.GraphResourceUriBeta + appConfig.Tenant);
_graphClient = new GraphService(serviceRoot,
async () => await AcquireTokenAsyncForUser(appConfig.GraphResourceUri, appConfig.Tenant));
return _graphClient;
}
private async Task<string> AcquireTokenAsyncForUser(string resource, string tenantId)
{
AuthenticationResult authenticationResult = await GetAccessToken(resource, tenantId);
_accessCode = authenticationResult.AccessToken;
return _accessCode;
}
private async Task<AuthenticationResult> GetAccessToken(string resource, string tenantId)
{
string authority = appConfig.Authority;
AuthenticationContext authenticationContext = new AuthenticationContext(authority);
ClientCredential credential = new ClientCredential(appConfig.ClientId, appConfig.ClientSecret);
string authHeader = HttpContext.Current.Request.Headers["Authorization"];
string userAccessToken = authHeader.Substring(authHeader.LastIndexOf(' ')).Trim();
UserAssertion userAssertion = new UserAssertion(userAccessToken);
var authenticationResult = await authenticationContext.AcquireTokenAsync(resource, credential, userAssertion);
return authenticationResult;
}
However if I change the SendEmailAsUserAsync method as shown below, the email is sent but an InvalidOperationException is thrown with message "The complex type 'System.Object' has no settable properties."
public async Task SendEmailAsUserAsync(EmailMessage message)
{
try
{
var graphClient = await _authenticationHelper.GetGraphClientAsync();
Message m = InitializeMessage(message);
//await graphClient.Me.SendMailAsync(m, true); //This did not work
var user = await graphClient.Me.ExecuteAsync();
await user.SendMailAsync(m, true);
}
catch (DataServiceQueryException dsqe)
{
_logger.Error("Could not get files: " + dsqe.InnerException.Message, dsqe);
throw;
}
}
Can any one point out if there is something wrong here.
Check the example project below, this has a working example (after you fill in the ClientID etc. in app.config).
Office 365 API demo applications
For sending email it uses the function below, which works if you set it up correctly. It also has a number of functions for Authenticating using Authorization Code Grant Flow.
public async Task SendMail(string to, string subject, string body)
{
var client = await this.AuthenticationHelper
.EnsureOutlookServicesClientCreatedAsync(
Office365Capabilities.Mail.ToString());
Message mail = new Message();
mail.ToRecipients.Add(new Recipient()
{
EmailAddress = new EmailAddress
{
Address = to,
}
});
mail.Subject = subject;
mail.Body = new ItemBody() { Content = body, ContentType = BodyType.HTML };
await client.Me.SendMailAsync(mail, true);
}
Actually, there is no assembly wrapper for the graph API.
Microsoft.Graph.dll is deprecrated.
So, you should to :
Deal with the REST requests : See here : http://graph.microsoft.io/docs/api-reference/v1.0/api/message_send
Generate a wrapper with Microsoft.Vipr project : see here : https://github.com/microsoft/vipr
For the authentication, ADAL works fine :)