I'm writing some code that will send 2 emails (using EXM) when a user updates their email address. 1 email goes to the original email address, and the other goes to the new email address.

The problem I'm having is that both emails are being sent to the new email address. I suspect what's happening is that the 2 messages get queued, and by the time it gets around to sending the first email, it looks at xDB for the user's email address and it's already been updated.

Any ideas on how I can make sure that the first email gets sent using the original email address?

  • Consider creating a new contact with the old email address and update the existing contact with the new email address. Commented Jan 6, 2020 at 11:03
  • When you say you are "writing some code" can you provide any details on the code or the code so it is clear how you are trying to trigger this?
    – ToddB
    Commented Jan 6, 2020 at 14:40

2 Answers 2


I ended up figuring out a solution.

What I did was to add a custom token to the first message that goes to the original email. That custom token was something like "forcedEmailAddress", which had a value of the original email address.

Then I used a pipeline to intercept FillEmail (which I was already doing for another purpose - allowing HTML in my tokens). In that pipeline, I'd check for the presence of my custom token, and if it was there, I'd change the value of message.To.

Here's the .config for the pipeline:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
        <processor type="Sitecore.EmailCampaign.Cm.Pipelines.SendEmail.FillEmail, Sitecore.EmailCampaign.Cm">
          <patch:attribute name="type">MyNamespace.CustomFillEmail, MyNamespace.Foundation.Exm</patch:attribute>

Here's my pipeline code:

using System.Web;
using Sitecore.EDS.Core.Dispatch;
using Sitecore.EmailCampaign.Cm.Pipelines.SendEmail;
using Sitecore.ExM.Framework.Diagnostics;
using Sitecore.Modules.EmailCampaign.Core.Crypto;
using Sitecore.Modules.EmailCampaign.Messages;

namespace MyNamespace.Foundation.Exm.Pipelines

    public class CustomFillEmail
        private readonly IStringCipher _cipher;
        private readonly ILogger _logger;

        public CustomFillEmail(IStringCipher cipher, ILogger logger)
            _cipher = cipher;
            _logger = logger;

        public void Process(SendMessageArgs args)
            if (args.EcmMessage == null)

        private MailFiller GetMailFiller(SendMessageArgs args)
            var ecmMessage1 = (HtmlMailBase) args.EcmMessage;
            if (ecmMessage1 != null)
                // here we return custom mail filler
                return new CustomHtmlMailFiller(ecmMessage1, args, _logger, _cipher);
            var ecmMessage2 = (MailMessageItem) args.EcmMessage;
            return ecmMessage2 != null ? new MailMessageFiller(ecmMessage2, args, _cipher) : null;

    public class CustomHtmlMailFiller : HtmlMailFiller
        private readonly HtmlMailBase _htmlMailBase;

        public CustomHtmlMailFiller(HtmlMailBase message, SendMessageArgs args, ILogger logger, IStringCipher cipher) :
            base(message, args, logger, cipher)
            if (message.CustomPersonTokens.TryGetValue(Constants.ForcedEmailRecipientAddress, out var forcedEmailRecipientAddress))
                message.To = forcedEmailRecipientAddress.ToString();

            _htmlMailBase = message;

        protected override void FillBody()
            var body = _htmlMailBase.Body;

            Email.ContentType = MessageContentType.Html;
            Email.HtmlBody = HttpUtility.HtmlDecode(_htmlMailBase.ReplaceTokens(body));
            Email.PlainTextBody = _htmlMailBase.ReplaceTokens(_htmlMailBase.AlternateText);

My message sender class:

using System;
using System.Collections.Generic;
using System.Linq;
using Sitecore.Analytics;
using Sitecore.Analytics.Tracking;
using Sitecore.Configuration;
using Sitecore.EmailCampaign.Cd.Services;
using Sitecore.EmailCampaign.Model.Messaging;
using Sitecore.XConnect;
using Sitecore.XConnect.Client;
using Sitecore.XConnect.Client.Configuration;
using Sitecore.XConnect.Collection.Model;
using MyNamespace.Portal.Domain.Model.Member;

namespace MyNamespace.Foundation.Exm
    public class MessageSender
        private readonly IClientApiService _clientApiService;

        public MessageSender(IClientApiService clientApiService)
            _clientApiService = clientApiService;

        public void Send(Member member, Guid messageId, IDictionary<string, object> dictionaryOfTokens, bool forceEmailAddressFromMember = false, string language = "en")

            if (Tracker.IsActive)
                var trackerContact = Tracker.Current.Contact;

                if (trackerContact?.Identifiers != null)
                    using (var client = SitecoreXConnectClientConfiguration.GetClient())

                        var contactIdentifier = trackerContact.Identifiers.FirstOrDefault(id => id.Source == Constants.ContactIdentifierSource);
                        if (contactIdentifier == null)

                        var reference = new IdentifiedContactReference(contactIdentifier.Source, contactIdentifier.Identifier);
                        var facets = new[] { PersonalInformation.DefaultFacetKey,EmailAddressList.DefaultFacetKey };
                        var expand = new ContactExpandOptions(facets);
                        var contact = client.Get(reference, expand);

                        var personal = contact.Personal();
                        if (contact.Personal() == null) personal = new PersonalInformation();
                        personal.Birthdate = member.DateOfBirth;
                        personal.FirstName = member.GivenNames;
                        personal.LastName = member.Surname;
                        personal.Title = member.Title;
                        personal.Gender = member.Gender.ToString();

                        client.SetPersonal(contact, personal);

                        var emails = contact.Emails();
                        if (contact.Emails() == null) emails = new EmailAddressList(new EmailAddress(member.EmailAddress, true), Constants.EmailPreferredKey);
                        emails.PreferredEmail.SmtpAddress = member.EmailAddress;

                        client.SetEmails(contact, emails);


                        if (Factory.CreateObject("tracking/contactManager", true) is ContactManager manager)
                            manager.RemoveFromSession(Tracker.Current.Contact.ContactId); // Use tracker ID, not xConnect ID
                            Tracker.Current.Session.Contact = manager.LoadContact(Tracker.Current.Contact.ContactId);

                        if (forceEmailAddressFromMember)
                            if(dictionaryOfTokens == null) dictionaryOfTokens = new Dictionary<string, object>();

                            dictionaryOfTokens.Add(Constants.ForcedEmailRecipientAddress, member.EmailAddress);

                        var messageInfo = new AutomatedMessage
                            MessageId = messageId,
                            ContactIdentifier = contact.Identifiers.FirstOrDefault(id => id.Source == Constants.ContactIdentifierSource),
                            CustomTokens = dictionaryOfTokens,
                            TargetLanguage = language




And I call the message sender like this:

var messageSender = new MessageSender(_clientApiService);

if (isEmailDirty)
  • 1
    Would be nice to post a snippet of your code to help others :) Commented Jan 7, 2020 at 7:11

Problem is that you are not storing old email and email is getting updated by new email.

You can follow the below steps:

  1. Create custom facet for old email which you can follow from here
  2. So before updating email from a new email, you need to update old email facet first.
  3. Then you can update email which is available in Email facets.

enter image description here 4. Now you have two fields new email and old email and you can send an email to both of them.

  • 3
    I don't think this will work. Also there's no need for a custom facet as the EmailAddresses facet has a property Others that could be used for the old email. Commented Jan 6, 2020 at 11:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.