Send Emails with Adobe Campaign Marketing Cloud (Neolane)

Hi this time instead of downloading data from Neolane or updating recipients in it , we want to use Neolane as email cannon leveraging its powerful template engine to manage the email look & feel and deciding on the fly ,using Neolane API, the targets of our mass email campaign.

So the use case is the following : define in Neolane an email template with some fields mapping and leverage Neolane API to send email using this template but defining the email recipients externally and also the contents of the mapped fields .

According to the official Adobe documentation this can be done using the Neolane Business Oriented Apis (we looked into the Data Oriented Apis in our previous articles) as specified here:

https://docs.campaign.adobe.com/doc/AC6.1/en/CFG_API_Business_oriented_APIs.html#SubmitDelivery__nms-delivery_

Unfortunately the documentation is not really clear/complete and I had really to dig inside adobe logs, error codes and soap responses to have this working properly, and here is some sample code that can help you.

The code is made using the sample provided inside the Adobe documentation (external file mapping with data coming from the CDATA section inside the delivery xml tag structure).

Here the c# code:




 string sessionToken = "Look at other Neolane Code samples on how retrieve the session token";
 string securityToken = "Look at the other Neolane Code samples on how retrieve the security token";

string scenario ="Write here the internal name of the delivery template";

 HttpWebRequest reqData = (HttpWebRequest)WebRequest.Create(adobeApiUrl);

 reqData.ContentType = "text/xml; charset=utf-8";
 reqData.Headers.Add("SOAPAction", "nms:delivery#SubmitDelivery");
 reqData.Headers.Add("X-Security-Token", securityToken);
 reqData.Headers.Add("cookie", "__sessiontoken=" + sessionToken);
 reqData.Method = "POST";

 


 string strWriteHeader = "<?xml version='1.0' encoding='ISO-8859-1'?>" +
 "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:urn=\"urn:nms:delivery\">" +
 "<soapenv:Header/>" + 
 " <soapenv:Body>"+
 "<urn:SubmitDelivery>" +
 "<urn:sessiontoken>" + sessionToken + "</urn:sessiontoken>" +
 " <urn:strScenarioName>" +scenario+ "</urn:strScenarioName>"+
 "<urn:elemContent>";

 string strWriteRecipientBody = "<delivery> " +
   "<targets fromExternalSource=\"true\"> " +
           "<externalSource><![CDATA[MsgId|ClientId|Title|Name|FirstName|Mobile|Email|Market_segment|Product_affinity1|Product_affinity2|Product_affinity3|Product_affinity4|Support_Number|Amount|Threshold " + 
System.Environment.NewLine+ 
"1|000001234|M.|Phulpin|Hervé|0650201020|herve.phulpin@adobe.com|1|A1|A2|A3|A4|E12|120000|100000]]></externalSource>" +
          "</targets> " +
"</delivery>";
 string strWriteFooter = " </urn:elemContent>" +
 "</urn:SubmitDelivery>" +
 "</soapenv:Body>" +
 "</soapenv:Envelope>";

 string bodyData = strWriteHeader + strWriteRecipientBody + strWriteFooter;

 byte[] byteArrayData = Encoding.UTF8.GetBytes(bodyData);

 // Set the ContentLength property of the WebRequest.
 reqData.ContentLength = byteArrayData.Length;
 // Get the request stream.
 Stream dataStreamInputData = reqData.GetRequestStream();
 // Write the data to the request stream.
 dataStreamInputData.Write(byteArrayData, 0, byteArrayData.Length);
 // Close the Stream object.
 dataStreamInputData.Close();

 var responseData = reqData.GetResponse();

 Stream dataStreamData = responseData.GetResponseStream();
 // Open the stream using a StreamReader for easy access.
 StreamReader readerData = new StreamReader(dataStreamData);
 // Read the content.
 string responseFromServerData = readerData.ReadToEnd();

 // Clean up the streams and the response.
 readerData.Close();
 responseData.Close();

return responseFromServerData;

Integration with Adobe Campaign Marketing (aka Neolane) Part II

Hi in the previous post we saw how to read information from Adobe Campaign Marketing.

This time I want to show you how to “write” to it, in particular how to add or modify recipients . This is , I believe, something that you want to do regularly to have in sync , for example, your users preferences on your sites and their current status on your campaign database . In fact a user that removes from his profile on a site the consensus to receive a specific newsletter imagines that automatically , from that moment, he will never be disturbed again. If you do not sync this asap, you have the risk to contact someone that does not want to be contacted . On the other side, if a new user registers on your site you want asap to have in your campaign tool to target him .

Here the c# code:


string adobeApiUrl = ConfigurationManager.AppSettings["adobeApiUrl"];
//Here for testing purpouses username and password are simply read by conf settings but you should acquire it in a secure way!
string adobeUser = ConfigurationManager.AppSettings["adobeUser"];
string adobePass = ConfigurationManager.AppSettings["adobePass"];
//We need to write recipients but they stay inside a folder so we have to know in advance the folder id or name
string adobeFolderId = ConfigurationManager.AppSettings["adobeFolderId"];
//Create the web request to the soaprouter page
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(adobeApiUrl);
req.Method = "POST";
req.ContentType = "text/xml; charset=utf-8";
//Add to the headers the requested Service (session) that we want to call
req.Headers.Add("SOAPAction", "xtk:session#Logon");

string userName = adobeUser;
string pass = adobePass;
//We craft the soap envelope creating a session Logon reequest
string body = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:urn=\"urn:xtk:session\">" +
"<soapenv:Header/><soapenv:Body><urn:Logon>" +
"<urn:sessiontoken/>" +
"<urn:strLogin>" + userName + "</urn:strLogin>" +
"<urn:strPassword>" + pass + "</urn:strPassword>" +
"<urn:elemParameters/>" +
"</urn:Logon></soapenv:Body></soapenv:Envelope>";
//We write the body to a byteArray to be passed with the Request Stream
byte[] byteArray = Encoding.UTF8.GetBytes(body);

// Set the ContentLength property of the WebRequest.
req.ContentLength = byteArray.Length;
// Get the request stream.
Stream dataStreamInput = req.GetRequestStream();
// Write the data to the request stream.
dataStreamInput.Write(byteArray, 0, byteArray.Length);
// Close the Stream object.
dataStreamInput.Close();

var responseAdobe = req.GetResponse();

Stream dataStream = responseAdobe.GetResponseStream();
// Open the stream using a StreamReader for easy access.
StreamReader reader = new StreamReader(dataStream);
// Read the content.
string responseFromServer = reader.ReadToEnd();
// Display the content.
// Clean up the streams and the response.
reader.Close();
responseAdobe.Close();
//Manually parsing the response with an XMLDoc
System.Xml.XmlDocument xResponse = new XmlDocument();
xResponse.LoadXml(responseFromServer);
// We parse manually the response. This is again for testing purpouses
XmlNode respx = xResponse.DocumentElement.FirstChild.FirstChild;

string sessionToken = respx.FirstChild.InnerText;
string securityToken = respx.LastChild.InnerText;

// We have done the login now we can actually do a query on Neolane
HttpWebRequest reqData = (HttpWebRequest)WebRequest.Create(adobeApiUrl);
reqData.ContentType = "text/xml; charset=utf-8";
//Add to the headers the requested Service (persist) that we want to call
reqData.Headers.Add("SOAPAction", "xtk:persist#Write");
reqData.Headers.Add("X-Security-Token", securityToken);
reqData.Headers.Add("cookie", "__sessiontoken=" + sessionToken);
reqData.Method = "POST";
//We craft the soap header also here session token seems to be needed
string strWriteHeader = "<?xml version='1.0' encoding='ISO-8859-1'?>" +
"<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:urn='urn:xtk:session'>" +
"<soapenv:Header/>" +
"<soapenv:Body>" +
"<urn:Write>" +
"<urn:sessiontoken>" + sessionToken + "</urn:sessiontoken>" +
"<urn:domDoc>";

string strWriteRecipientBody = string.Empty;
string strWriteFooter = "</urn:domDoc>" +
"</urn:Write>" +
"</soapenv:Body>" +
"</soapenv:Envelope>";
//Here I loop inside a list of objects that represent the adobe recipient I want to write
// operation is insertOrUpdate and the key that will check if it is an insert or an update is the email in my case.
// you can pick the one that you think is good

foreach (AdobeRecipient recipient in updatesOnAdobe)
{
strWriteRecipientBody +=
"<recipient "
+ "_operation='insertOrUpdate' "
+ "_key='@email' "
+ "xtkschema='nms:recipient' "
+ "account='" + recipient.account + "' "
+ "lastName='" + recipient.lastName + "' "
+ "firstName='" + recipient.firstName + "' "
+ "email='" + recipient.email + "' "
+ "origin='" + recipient.origin + "' "
+ "company='" + recipient.company + "'>"
+ "<folder id='" + recipient.folderId + "'/> "
+ "</recipient> ";

}
//Full String ready to be passed
string bodyData = strWriteHeader + strWriteRecipientBody + strWriteFooter;

byte[] byteArrayData = Encoding.UTF8.GetBytes(bodyData);

// Set the ContentLength property of the WebRequest.
reqData.ContentLength = byteArrayData.Length;
// Get the request stream.
Stream dataStreamInputData = reqData.GetRequestStream();
// Write the data to the request stream.
dataStreamInputData.Write(byteArrayData, 0, byteArrayData.Length);
// Close the Stream object.
dataStreamInputData.Close();

var responseData = reqData.GetResponse();

Stream dataStreamData = responseData.GetResponseStream();
// Open the stream using a StreamReader for easy access.
StreamReader readerData = new StreamReader(dataStreamData);
// Read the content.
string responseFromServerData = readerData.ReadToEnd();
// Here we should receive an OK from Neolane
// Clean up the streams and the response.
readerData.Close();
responseData.Close();

return responseFromServerData;