DEV Community

Jan Hjørdie
Jan Hjørdie

Posted on

Integrating Campaign Monitor Smart Emails (Transactional) — Minimal Guide

This short guide describes a minimal, robust integration pattern for sending Campaign Monitor Smart (transactional) emails from a .NET application. It documents practical pitfalls and how the example CampaignMonitorService addresses them.

Note: all examples below use placeholders (no real API keys, email addresses, or list IDs).


Authentication

  • Campaign Monitor uses Basic Auth for all API calls. Use the API key as the username and any non-empty string (for example x) as the password.
  • There is no separate transactional API key; the same key used for classic APIs also works for transactional Smart Email endpoints.

Example header (curl style):

# placeholder values
API_KEY=<YOUR_API_KEY>
SMART_ID=<YOUR_SMART_EMAIL_ID>

curl -u "$API_KEY:x" \
  -H "Content-Type: application/json" \
  -d '{"To":["<recipient_placeholder>"],"Data":{"Subject":"Test"},"ConsentToTrack":"Yes"}' \
  https://api.createsend.com/api/v3.3/transactional/smartEmail/$SMART_ID/send
Enter fullscreen mode Exit fullscreen mode

Key implementation notes

  • Authentication precedence: prefer an explicit transactional API key if present, otherwise fall back to the general API key or the first configured client key.
  • Base URL normalization: some environments reference transact.createsend.com which can cause TLS subjectAltName mismatches. Normalize transactional calls to api.createsend.com to avoid local TLS errors.
  • Development TLS helper: a SkipCertificateValidation option can be enabled only in development to bypass name-mismatch TLS errors. Do not enable in production.

Smart Email payload shape

  • Smart Email sends are POSTs to /transactional/smartEmail/{SMART_ID}/send.
  • The JSON payload should contain:
    • To: array of recipient addresses
    • Data: object with variables available to the Smart Email template (Liquid)
    • ConsentToTrack: commonly required by templates/accounts (values: "Yes" or "No")

Example payload (placeholder values):

{
  "To": ["<recipient_placeholder>"],
  "Data": { "Subject": "My Subject", "EventsList": "<html string>" },
  "ConsentToTrack": "Yes"
}
Enter fullscreen mode Exit fullscreen mode

Sending strategy implemented in CampaignMonitorService

  • SendTransactionalEmailWithEventsAsync:

    • If a Smart Email id is supplied (or configured globally), it sends to the Smart Email transactional endpoint using the Data payload.
    • If no Smart Email id exists, it falls back to a legacy transactional API path.
    • The method handles empty response bodies (202 Accepted) and attempts to parse returned JSON when available.
  • SendTransactionalEmailWithEventsToListAsync (bulk list send):

    • Pages the classic API endpoint lists/{listId}/active.json to collect active subscribers.
    • De-duplicates recipients and chunks them into batches (default 200 recipients per batch).
    • Sends each chunk as a separate Smart Email request.
    • Returns a summary response containing counts of sent/failed batches and the last raw response body for troubleshooting.

Robust subscriber parsing

  • Some Campaign Monitor responses contain fields in unexpected formats. A common example is the Date field in list subscribers: it may be an empty string or a non-ISO value.
  • To avoid deserialization failures, parse the Results array manually and only attempt to parse Date when the value is non-empty and parseable. Malformed or empty dates should be treated as null.

HTML generation for list data

  • If you need to provide rendered HTML to a Smart Email template (for example a generated events list), produce a small, escaped HTML fragment (for example a <ul> with encoded text) and pass it as a Liquid variable in Data (the example uses EventsList).

Batching, throttling and retries

  • Default batch size in the example is 200 recipients. This keeps payloads manageable.
  • If you observe 429 or 5xx responses, add a delay between batches (or exponential backoff) and/or retries for failed batches.
  • Log batch-level results to spot systemic failures and to decide if retrying is appropriate.

Simple backoff example (concept):

// between batches
await Task.Delay(TimeSpan.FromMilliseconds(500));
// implement retry with exponential backoff for failed batch responses
Enter fullscreen mode Exit fullscreen mode

Logging and diagnostics

  • Log which credential source is used (but mask keys in logs).
  • Log the transactional client's Authorization header presence (mask preview) and the target BaseAddress.
  • Preserve RawBody of responses when present to aid debugging.

Example (C#) — sanitized

Below is a minimal example showing how a caller might use the helper that sends to a list. All values are placeholders.

// assumed injected: CampaignMonitorService cmService

var events = Enumerable.Range(1, 5)
    .Select(i => new CampaignMonitorService.EventInfo(DateTime.Today.AddDays(i), $"Event {i}", $"Desc {i}"))
    .ToList();

var listId = "<LIST_ID_PLACEHOLDER>";
var response = await cmService.SendTransactionalEmailWithEventsToListAsync(
    listId: listId,
    events: events,
    subject: "Upcoming Events",
    smartEmailId: null, // use configured default if null
    text: "Optional plain text",
    batchSize: 200
);

if (response != null)
    Console.WriteLine($"Send result: {response.Status}");
else
    Console.WriteLine("No recipients found or send failed.");
Enter fullscreen mode Exit fullscreen mode

Testing helpers

  • Use a dry-run script or curl to validate payload shapes before executing real sends.
  • Keep ConsentToTrack in the payload during testing to avoid 400 errors when the template expects consent.

This pattern and the defensive parsing/normalization steps make a Smart Email integration more resilient to real-world quirks: TLS host oddities, empty response bodies, non-standard JSON fields, and the need to send to multiple recipients in a controlled, observable way.

Top comments (0)