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
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.comwhich can cause TLS subjectAltName mismatches. Normalize transactional calls toapi.createsend.comto avoid local TLS errors. - Development TLS helper: a
SkipCertificateValidationoption 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"
}
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
Datapayload. - 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.
- If a Smart Email id is supplied (or configured globally), it sends to the Smart Email transactional endpoint using the
-
SendTransactionalEmailWithEventsToListAsync(bulk list send):- Pages the classic API endpoint
lists/{listId}/active.jsonto 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.
- Pages the classic API endpoint
Robust subscriber parsing
- Some Campaign Monitor responses contain fields in unexpected formats. A common example is the
Datefield in list subscribers: it may be an empty string or a non-ISO value. - To avoid deserialization failures, parse the
Resultsarray manually and only attempt to parseDatewhen the value is non-empty and parseable. Malformed or empty dates should be treated asnull.
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 inData(the example usesEventsList).
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
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
RawBodyof 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.");
Testing helpers
- Use a dry-run script or curl to validate payload shapes before executing real sends.
- Keep
ConsentToTrackin 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)