I am trying to discover if there is a way to determine the internet message ID after sending an email using the EWS Managed API. I understand you can go in there and get the results from the sent box, but the server that is sending these emails is sending multiple emails at a time from different services.
No you can't, basically because EWS sends message Asynchronously the Id isn't available see https://social.msdn.microsoft.com/Forums/azure/en-US/dd034b8c-ffa1-4ae0-9025-45fcf520c9e5/updateitem-does-not-return-itemid?forum=exchangesvrdevelopment
As a work around you might want to consider setting the Internet messageId on the Message before you send it. As long as it valid and unique it should work okay eg
ExtendedPropertyDefinition PidTagInternetMessageId = new ExtendedPropertyDefinition(4149, MapiPropertyType.String);
EmailMessage ema = new EmailMessage(service);
ema.Subject ="test from ews";
ema.Body = new MessageBody("test<br>Rgds<>");
ema.ToRecipients.Add("gscales#domain.com");
ema.SetExtendedProperty(PidTagInternetMessageId,("<" +Guid.NewGuid().ToString() + "#domain.com>"));
ema.SendAndSaveCopy();
Also if you save the message first as a draft before sending it the server will assign the MessageId property which which should then be able to read back using Load.
Cheers
Glen
Related
I am having an issue with sending emails specifically to Gmail-related accounts, and I'll be darned if I know what the issue is. This is a C# ASP.NET project, by the way.
First, the following code works, as long as I am sending to any email account OTHER than a Gmail account:
var mail = new MailMessage {
Subject = "test email",
Body = "this is only a test",
Priority = MailPriority.High,
IsBodyHtml = true,
From = new MailAddress ( "<outbound email here>" )
};
var msgID = Guid.NewGuid().ToString();
var sentBy="<outbound mail domain>";
mail.Headers.Add ( "message-id", $"<{msgID}>");
mail.Headers.Add ( "msg-id", $"<{msgID}#{sentBy}>");
mail.To.Add ( new MailAddress ( "<recipient email>" ) );
var smtpClient = new SmtpClient("<email server address>") {
Port = 587,
Credentials = new NetworkCredential("<sender's email address>", "<password>"),
};
smtpClient.Send ( mail );
I have removed email addresses and network credentials, obviously.
The code works, because as long as I send email to a NON-Gmail account, it comes through just fine. But anything going to a Gmail-related account never arrives.
I added the two lines in the code above to add a message ID to the header based on what I read in several older posts here about some mail servers, like Gmail, rejecting email messages that didn't include them, but it has not fixed the issue, and I'm out of ideas. My ISP says the SPF record for the mail server is fine, so according to them that's not the issue.
Has anyone else encountered this recently, and if so, how did you fix it?
To clarify, the comments/answers I have received so far are appreciated, but as I stated in the OP, this is a problem with sending emails TO Gmail accounts. I am using my ISP's mail server to send them, and I am adding a message ID to the header to address what the log says, that the message is missing a message ID and won't be accepted. I can send emails to other non Gmail accounts just fine, and when I inspect the headers they show a message id. So I don't know why this continues to be an issue.
So the answer to this is that the issue was related to changes Google made to policies for sending emails to Gmail accounts, which might require adjustments to the SPF record on the sending SMTP server. That was the case in this situation - the hosting company was slow to respond (took them more than a week) and had to elevate the issue to their Tier 3 techs, but once the SPF record was fixed to account for Google's changes, all was resolved.
I need to send an emails (bulk sending), and I have two cases:
The same email sent into multiple recipients
Different emails sent into different recipients
How, in both cases, I can control statuses?
First case will return single x-message-id, but how it can be translated into separate status for each recipients? (delivered, opened and so on?)
Second case, I believe, need to be done by sending one-by one, is that correct? Or there is also method to send bulk emails in that case?
I'm using C# and official .NET library.
To send the same email sent to multiple recipients:
You can use the MailHelper.CreateSingleEmailToMultipleRecipients and you'll need to specify the text and HTML content in code. (Sample)
You can also use the MailHelper.CreateSingleTemplateEmailToMultipleRecipients method which will use a SendGrid Dynamic Email Template. In that case the email template is already stored in SendGrid and you simply provide the ID of the template.
There's a showAllRecipients named parameter which defaults to false. When false recipients cannot see other recipients, when true recipients can see other recipients.
I'll talk about retrieving the status's of the individual emails later.
To send different email to different people:
You can use MailHelper.CreateMultipleEmailsToMultipleRecipients which will use a single text and HTML body template, but you can add substitution tags and pass in substitution data so the email will be personalized for every recipient. (Sample)
You can construct a single MailMessage but use multiple Personalization objects to override any of the MailMessage properties for specific recipients. (Sample)
You can also construct multiple MailMessage objects and submit them individually to the API, although the above options are made for this scenario.
To send any of these emails, your API key must have the mail.send permission.
There are additional ways to send bulk email with SendGrid, which I documented here in much more detail.
As you noted, when you send email in bulk, only a single message ID is returned via the x-message-id header.
When you later try to retrieve the details using that message ID, you will not find any messages.
That is because the message ID in the x-message-id header is used as a base ID and the real message IDs will append another ID.
For example, x-message-id will return "W86EgYT6SQKk0lRflfLRsA", but when you retrieve one of the individual messages, it will looks something like this: "W86EgYT6SQKk0lRflfLRsA.filterdrecv-5645d9c87f-78xgx-1-62841247-82.1".
You can retrieve these messages via the E-Mail Activity API like this:
var queryParams = JsonSerializer.Serialize(new
{
query = $"msg_id LIKE '{messageId}%'",
limit = 10
});
var response = await client.RequestAsync(
method: SendGridClient.Method.GET,
urlPath: "messages",
queryParams: queryParams
);
Console.WriteLine(await response.Body.ReadAsStringAsync());
Take note of the query property which is has the $"msg_id LIKE '{messageId}%'" value.
This query will filter to messages that begin with the message ID returned from the bulk email (messageId), and as a result retrieve all the individual messages.
You cannot query these messages immediately after submitting the bulk email to SendGrid's queue as it takes some time for them to become available through the API.
In my code I queried these messages every 30 seconds until the count of the recipients in the bulk email matched the messages returned.
The resulting JSON data looks like this:
{
"messages": [
{
"from_email": "some#email.address",
"msg_id": "5QSczogTRHqFtiIkLxMtWA.filterdrecv-5645d9c87f-6r2ch-1-62847C63-2D.0",
"subject": "Sending with Twilio SendGrid is Fun",
"to_email": "some#email.address",
"status": "delivered",
"opens_count": 0,
"clicks_count": 0,
"last_event_time": "2022-05-18T05: 01: 05Z"
},
{
"from_email": "some#email.address",
"msg_id": "5QSczogTRHqFtiIkLxMtWA.filterdrecv-5645d9c87f-6r2ch-1-62847C63-2D.1",
"subject": "Sending with Twilio SendGrid is Fun",
"to_email": "some#email.address",
"status": "delivered",
"opens_count": 0,
"clicks_count": 0,
"last_event_time": "2022-05-18T05: 01: 05Z"
},
...
]
}
As you can see, this includes the status property.
Note: You must purchase additional email activity history to gain access to the Email Activity Feed API.
Note: To retrieve message via the Email Activity Feed API, your API key must have the email_activity.read permission.
This would be one way to retrieve the status of your email messages.
Another way to keep track of the status of email messages would be to create a public ASP.NET endpoint configure the URL as the SendGrid Event Webhook.
SendGrid will send an HTTP request to your ASP.NET endpoint for every event which you can use to update the status of email messages.
PS: You may already be doing this, but whether you're sending a single email to multiple recipients or multiple emails to multiple recipients, Twilio recommends setting the SendAt property on the SendGridMessage when sending bulk emails.
Quoting from the SendGrid docs:
This technique allows for a more efficient way to distribute large
email requests and can improve overall mail delivery time performance.
This functionality:
Improves efficiency of processing and distributing large volumes of email.
Reduces email pre-processing time.
Enables you to time email arrival to increase open rates.
Is available for free to all SendGrid customers.
I hope that answered all your questions, if not let me know. I can't wait to see what you build!
I am using mail kit to send receive mail, and manage all records in my database. I am storing all user action for particular mail and then execute it using my code.
I am storing message id in the table for the unique message, now I want to get the message using messageid. Is there any way to do it?
Firstly, don't expect the Message-Id header to be globally unique. Any hacker could easily create their own message and re-use a known Message-Id to try and confuse software that depends on Message-Ids being unique.
That said, you'll need to use the IMailFolder.Search() API combined with SearchQuery.HeaderContains() to search for messages with a particular Message-Id header.
var uids = folder.Search (SearchQuery.HeaderContains ("Message-Id", "blah#blah.com"));
I've using a custom GetMailTips SOAP call (since the EWS for Core 2.0 doesn't support it) to get Out of Office info for a batch of email addresses.
How can I get the display names of the users that I am passing in the email address for?
I can call ResolveName of the managed API and that works but it has to be done one at a time and that is slow. I would like to ideally get this info out when I make my GetMailTips request and failing that make a call with all the email addresses to get the Display Names all at once. I read there is meant to be a ResolveNames method but that's not in the API either.
Any help appreciated
Autodiscover can return that for multiple users eg
AutodiscoverService adService = new AutodiscoverService(ExchangeVersion.Exchange2013_SP1);
adService.Credentials = new NetworkCredential("user#d.com", "pass");
adService.RedirectionUrlValidationCallback = adAutoDiscoCallBack;
List<String> Users = new List<string>();
Users.Add("user1#domain.com");
Users.Add("user2#domain.com");
GetUserSettingsResponseCollection usrSettings = adService.GetUsersSettings(Users, UserSettingName.UserDisplayName);
foreach(GetUserSettingsResponse usr in usrSettings)
{
Console.WriteLine(usr.Settings[UserSettingName.UserDisplayName]);
}
Another way would be to create a Message and add the email address as recipients then save it to the drafts folders and the address should get resolved against the GAL.
I have an email with yahoo business and MailKit works with POP. I want to download the message after finding a specific subject. Or could I use IMAP?
If the POP3 server supports the TOP extension, you can download just the message headers to first check the subject. To do that, you could do something like this:
if (client.Capabilities.HasFlag (Pop3Capabilities.Top)) {
var headers = client.GetMessageHeaders (index);
if (headers[HeaderId.Subject] == subject)
message = client.GetMessage (index);
}
If your Yahoo account also supports IMAP, I would recommend using IMAP since IMAP allows you to query the server for messages with a given subject which is much more efficient than downloading the headers for every message to check if the subject matches the one you are looking for.