Working With The MailChimp API In .NET: Campaign Management

This is the sixth in a series of articles about using the MailChimp.NET.V3 NuGet package to interact with the MailChimp REST API.

We have a MailChimp List, and that List contains at least one Member. Now we can go ahead and send a Campaign.

In MailChimp, a Campaign consists of two basic parts: Content, or whatever it is you plan to email; and a List to which it will be mailed.

Content can be plain text, HTML, or a Template. (We can also create RSS-driven campaigns.) Additionally, we can specify a given Segment of a List to which we want the campaign sent; either a previously saved Segment, or a Segment we create prior to sending the Campaign and don’t save.

We can also configure the Campaign to include tracking, analytics, folders and so on.

I’m not going to cover those in this tutorial, but it’s worth mentioning that the MailChimp.NET.V3 library has support for all those features.

Create a Campaign

To create a MailChimp campaign via the API, we first instantiate, then populate the properties of, a Campaign object. Then, we call the MailChimpManager.Campaigns.AddOrUpdateAsync method.

using MailChimp.Net;
using MailChimp.Net.Core;
using MailChimp.Net.Models;
using System;
using System.Net;
using System.Threading.Tasks;
using System.Web.Mvc;

namespace MailChimpNetDemo.Controllers
{
    public class MailChimpCampaignController : Controller
    {
        private static MailChimpManager Manager = new MailChimpManager();

        public async Task<ActionResult> Create()
        {
            var campaign = new Campaign
            {
                Type = CampaignType.Regular,
                Recipients = new Recipient
                {
                    ListId = "f809a0eba9"
                },
                Settings = new Setting
                {
                    SubjectLine = $"Campaign created by program at {DateTime.UtcNow.ToString("s")}",
                    Title = $"Dynamic campaign {Guid.NewGuid()}",
                    FromName = "Doug Vanderweide",
                    ReplyTo = "mailchimp@dougv.com"
                },
                Tracking = new Tracking
                {
                    Opens = true,
                    HtmlClicks = true,
                    TextClicks = true
                },
                SocialCard = new SocialCard
                {
                    ImageUrl = "http://cdn.smosh.com/sites/default/files/legacy.images/smosh-pit/122010/lolcat-link.jpg",
                    Description = "I'm learning how to make dynamic MailChimp campaigns via the API.",
                    Title = "Using the MailChimp API in .NET via the MailChimp.NET.V3 wrapper"
                },
                ReportSummary = new ReportSummary(),
                DeliveryStatus = new DeliveryStatus()
            };

            try
            {
                await Manager.Campaigns.AddOrUpdateAsync(campaign);
                return RedirectToAction("Index");
            }
            catch (MailChimpException mce)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadGateway, mce.Message);
            }
            catch (Exception ex)
            {
                return new HttpStatusCodeResult(HttpStatusCode.ServiceUnavailable, ex.Message);
            }
        }
    }
}

Note that we assign the Campaign to a List. All Campaigns must belong to a List; you can’t have a “generic” Campaign.

Adding dynamic Content to a Campaign

With the Campaign created, we can now add content to it.

One way to do that is to specify the HTML and plain text you want to use. To do that, we create a ContentRequest object, setting its Html and PlainText properties, respectively.

We then call MailChimpManager.Content.AddOrUpdateAsync, specifying the Campaign Id and the ContentRequest object we just created.

public async Task<ActionResult> SetContentRaw()
{
	var content = new ContentRequest
	{
		PlainText = "Hello world! I am testing Doug Vanderweide's MailChimp.NET.V3 demo, at https://www.dougv.com",
		Html = "<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><title>title</title></head><body><p>Hello world! I am testing Doug Vanderweide's MailChimp.NET.V3 demo, at <a href=\"https://www.dougv.com\">https://www.dougv.com</a></body></html>"
	};

	try
	{
		await Manager.Content.AddOrUpdateAsync("d4688e21b2", content);
		return RedirectToAction("Detail");
	}
	catch (MailChimpException mce)
	{
		return new HttpStatusCodeResult(HttpStatusCode.BadGateway, mce.Message);
	}
	catch (Exception ex)
	{
		return new HttpStatusCodeResult(HttpStatusCode.ServiceUnavailable, ex.Message);
	}
}

Note that at Lines 5 and 6, I set the PlainText and HTML values for my content with strings. I could just as easily have called a TextReader / StreamReader here and picked up one or more of those values from a file:

using (var reader = File.OpenText("content-source-file.html"))
{
	content.Html(reader.ReadToEnd());
}

Finally, note that I can set the Content of a Campaign as many times as I like, overriding previous content.

Setting Content via a Template

At this writing, there is a bug in MailChimp.NET.V3 that does not allow for the setting of a Template as Campaign Content.

I expect that will be cleared up and will edit this section once it is ready.

Update a Campaign

Updating a Campaign does not alter its Content; rather, it only affects the List to which the Campaign is assigned, along with other metadata.

As with creating a Campaign, we need to provide Campaign object; in this case, including the Id of the campaign we want to alter.

However, we only need to populate the other Campaign properties that we want altered; if we don’t specify an updated value for a Campaign property, its old value will be retained.

public async Task<ActionResult> Update()
{
	var campaign = new Campaign
	{
		Id = "d4688e21b2",
		Settings = new Setting
		{
			Title = $"Dynamic campaign {Guid.NewGuid()} modified {DateTime.UtcNow.ToString("s")}"
		}
	};

	try
	{
		await Manager.Campaigns.AddOrUpdateAsync(campaign);
		return RedirectToAction("Detail");
	}
	catch (MailChimpException mce)
	{
		return new HttpStatusCodeResult(HttpStatusCode.BadGateway, mce.Message);
	}
	catch (Exception ex)
	{
		return new HttpStatusCodeResult(HttpStatusCode.ServiceUnavailable, ex.Message);
	}
}

Test a Campaign

I highly recommend testing a campaign before you send it.

Normally I would also recommend calling the Send Checklist before testing a Campaign. While I see an implementation of the Send Checklist in the MailChimp.NET.V3 library, I don’t see a public MailChimpManager method that exposes it, so I am unsure how to call it.

That said, if you run a Campaign test prior to sending, you should be able to verify that your Campaign is good to go.

With your Campaign’s Content properly set, you can test it. To do that, you’ll need at least one email address; if you want to use more (such as to ensure PlainText works OK), you provide them as a string array.

public async Task<ActionResult> Test()
{
	try
	{
		await Manager.Campaigns.TestAsync("d4688e21b2", new CampaignTestRequest
		{
			Emails = new string[] { "foo@bar.com", "bar@foo.com" },
			EmailType = "html"
		});

		return RedirectToAction("Detail");
	}
	catch (MailChimpException mce)
	{
		return new HttpStatusCodeResult(HttpStatusCode.BadGateway, mce.Message);
	}
	catch (Exception ex)
	{
		return new HttpStatusCodeResult(HttpStatusCode.ServiceUnavailable, ex.Message);
	}
}

After you run your test, check the email accounts to which you sent the test; it should receive the messages shortly, and prefix the subject line with the word [Test].

Send a Campaign

And finally, the payoff: Sending a Campaign.

All we need to do is provide the MailChimpManager.Campaigns.SendAsync method the ID of our campaign.

public async Task<ActionResult> Send()
{
	try
	{
		await Manager.Campaigns.SendAsync("d4688e21b2");
		return RedirectToAction("Detail");
	}
	catch (MailChimpException mce)
	{
		return new HttpStatusCodeResult(HttpStatusCode.BadGateway, mce.Message);
	}
	catch (Exception ex)
	{
		return new HttpStatusCodeResult(HttpStatusCode.ServiceUnavailable, ex.Message);
	}
}

View a Campaign’s details

Looking at a Campaign’s properties is as simple as providing its Id to the MailChimpManager.Campaigns.GetAsync method.

public async Task<ActionResult> Detail()
{
	var model = await Manager.Campaigns.GetAsync("d4688e21b2");
	return View(model);
}

Here’s the View I am using, which does not show all the properties of a Campaign object; just the ones I think are most relevant.

@model MailChimp.Net.Models.Campaign
@{
    ViewBag.Title = $"Detail for campaign ID {Model.Id}";
}

<h2>Detail for Campaign ID @Model.Id</h2>

<dl class="row">
    <dt class="col-md-3 text-right">Title</dt>
    <dd class="col-md-9">@Model.Settings.Title</dd>
	
    <dt class="col-md-3 text-right">Content Type</dt>
    <dd class="col-md-9">@Model.ContentType</dd>
	
    <dt class="col-md-3 text-right">Create Time</dt>
    <dd class="col-md-9">@Model.CreateTime.ToString("s")</dd>
	
    <dt class="col-md-3 text-right">Status</dt>
    <dd class="col-md-9">@Model.Status</dd>
	
    <dt class="col-md-3 text-right">Delivery Status</dt>
    <dd class="col-md-9">
    @if (Model.DeliveryStatus.Status != null) { 
        @Model.DeliveryStatus.Status 
    }
    else
    { 
        @Html.Raw("NULL") 
    }
    </dd>
	
    <dt class="col-md-3 text-right">Type</dt>
    <dd class="col-md-9">@Model.Type</dd>
</dl>

List all Campaigns

You can generate a list of all Campaigns by creating a CampaignRequest class to filter your results, then calling MailChimpManager.Campaigns.GetAllAsync with that filter as its argument.

In my case, I’m going to get a list of the last 10 saved Campaigns for a specific List.

public async Task<ActionResult> Index()
{
	var options = new CampaignRequest
	{
		ListId = "f809a0eba9",
		Status = CampaignStatus.Save,
		SortOrder = CampaignSortOrder.DESC,
		Limit = 10
	};

	ViewBag.ListId = "f809a0eba9";

	try
	{
		var model = await Manager.Campaigns.GetAllAsync(options);
		return View(model);
	}
	catch (MailChimpException mce)
	{
		return new HttpStatusCodeResult(HttpStatusCode.BadGateway, mce.Message);
	}
	catch (Exception ex)
	{
		return new HttpStatusCodeResult(HttpStatusCode.ServiceUnavailable, ex.Message);
	}
}

Here’s the relatively simple View:

@model IEnumerable<MailChimp.Net.Models.Campaign>
@{
    ViewBag.Title = $"Campaigns for list ID {ViewBag.ListId}";
}

<h2>Campaigns for List ID @ViewBag.ListId</h2>

<table class="table table-striped table-bordered">
    <thead>
        <tr>
            <th>ID</th>
            <th>Title</th>
            <th>Emails Sent</th>
            <th>View</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var campaign in Model)
        {
            <tr>
                <td>@campaign.Id</td>
                <td>@campaign.Settings.Title</td>
                <td>@campaign.EmailsSent</td>
                <td><a href="@campaign.ArchiveUrl" target="_viewCampaign">View &raquo;</a></td>
            </tr>
        }
    </tbody>
</table>

Delete a Campaign

Warning: Deleting a Campaign via the API is instant and permanent. Be careful.

Deleting a Campaign is basically the same as getting its details: Just provide an ID to the MailChimpManager.Campaigns.DeleteAsync method.

public async Task<ActionResult> Delete()
{
	try
	{
		await Manager.Campaigns.DeleteAsync("d4688e21b2");
		return RedirectToAction("Index");
	}
	catch (MailChimpException mce)
	{
		return new HttpStatusCodeResult(HttpStatusCode.BadGateway, mce.Message);
	}
	catch (Exception ex)
	{
		return new HttpStatusCodeResult(HttpStatusCode.ServiceUnavailable, ex.Message);
	}
}

And that’s it for the series. I hope you enjoyed it.

Github repo for all the code in these demos: https://github.com/dougvdotcom/MailChimpNetDemo

Featured photo by JudaM via Pixabay, in the public domain.
Featured photo by JudaM via Pixabay, in the public domain.

8 Comments

  1. Hi Doug Vanderweide,

    Thanks for such a great blog regarding Mail Chimp from basic setup to send campaign. I found all the information looking for and better way to use Mail Chimp from your blog.

    I need your help or opinion for following scenario:-

    We are developing application for our client using Mail Chimp Version 3.0 by Brandon Seydel .Net wrapper. I attached the screen shot of Mail Chimp .Net wrapper used in application.

    My client require additional functionality to send unique or sometime multiple attachment to each subscriber added in list while we send campaign.

    Please suggest us appropriate solution.

  2. @Gusharan: Per the TOS, you cannot send transactional email through MailChimp.

    As the link explains, transactional email is any message that is specific to a person. Since you intend to send to your customers emails that are specific to them, with attachments, you need a transactional email service.

    You can use Mandrill, which is MailChimp’s transactional email service, to this end. There are other transactional email providers out there, however, notably SendGrid.

  3. All tutorials aboul interaction with was great! I really have learnt a lot!
    I have an issue/question and I don’t know how to solve it.
    In the above code you are using the entire list of recipients:

    Recipients = new Recipient
                    {
                        ListId = "f809a0eba9"
                    },
    

    if I would like to send to filtered recipients, how this could be done? How can I filter based on Interestings and Groups?
    Thank you

  4. Hello,

    I am following your example and when I get to the content upload part, I receive this error “Unexpected character encountered while parsing value: <. Path '', line 0, position 0."

    Everything I did I as wrote in this example and I am not too sure what's wrong with it …

    Thx for any help you could provide

  5. @Alex: The error is self-explanatory: You’re passing in a left-angle bracket (<) where one doesn’t belong.

    Since I can’t see your code I can’t help you. If you want to put it up in a pastebin or gist I can take a quick look. But just like you, I do not work for free.

  6. Like I said, it’s pretty much as your example which I used in a part of my application “as is” for starter then once I saw how the full cycle worked i’d do my own code.

    So here is the pastebin:
    https://pastebin.com/mGnG5fiJ

    the code send the error at the line “await mc_mngr.Content.AddOrUpdateAsync(aCampaign.Id, content);”

  7. Nevermind … I got it, pretty stupid error, forgot to get the campaign object back from the first call … so I had no ID which led to the error. The error itself isn’t too expressive actually

Leave a Reply

  • Check out the Commenting Guidelines before commenting, please!
  • Want to share code? Please put it into a GitHub Gist, CodePen or pastebin and link to that in your comment.
  • Just have a line or two of markup? Wrap them in an appropriate SyntaxHighlighter Evolved shortcode for your programming language, please!