How to Bulk Cancel Pending Email Sends in Salesforce Marketing Cloud Engagement

I understand this almost never happens. Imagine if it does though, and you need to cancel a large number of pending sends to minimise the impact. Going through them one by one in the tracking sends would be very tedious, and slow enough that it defeats the purpose of acting quickly.

This post walks through a three-part solution: a staging Data Extension, a Query Activity that populates it with currently pending jobs, and an SSJS script that loops through and cancels them.

Step 1: Create the staging Data Extension

Create a Data Extension called CancelSends with the following fields:

JobID, EmailName, DeliveredTime, JobType, JobStatus, SchedTime,
EmailSubject, Category, SuppressTracking, SendClassificationType,
SendClassification, EmailSendDefinition, TriggeredSendCustomerKey

JobID should be a Number field, matching the type used in the _Job system data view. Set the Query Activity that populates this DE (below) to Overwrite mode. That matters for how the cancellation script behaves later: every time the automation runs, the DE starts fresh with only the jobs that are currently pending, so there’s no risk of the script re-processing a job it already cancelled on a previous run.

Step 2: Query Activity to find pending jobs

SELECT JobID, EmailName, DeliveredTime, JobType, JobStatus, SchedTime,
       EmailSubject, Category, SuppressTracking, SendClassificationType,
       SendClassification, EmailSendDefinition, TriggeredSendCustomerKey
FROM [_Job]
WHERE SuppressTracking = 0
AND JobStatus = 'Queued'
AND DeliveredTime IS NULL
AND (
    /* Journey Builder sends */
    JourneyActivityObjectID IS NOT NULL OR
    /* Triggered Sends */
    (TriggeredSendCustomerKey IS NOT NULL AND JourneyActivityObjectID IS NULL) OR
    /* User Initiated Sends */
    TriggeredSendCustomerKey IS NULL
)Code language: PHP (php)

Step 3: The cancellation script

<script runat="server">
Platform.Load("core", "1.1.5");

try {
    var dataExtension = DataExtension.Init("CancelSends");
    var cols = ["JobID"];
    var records = dataExtension.Rows.Retrieve(cols);

    var results = { cancelled: 0, failed: 0, details: [] };

    if (records && records.length > 0) {
        for (var i = 0; i < records.length; i++) {
            var jobId = records[i]["JobID"];

            try {
                var mySend = Send.Init(jobId);
                var status = mySend.CancelSend();

                if (status == "OK") {
                    results.cancelled++;
                } else {
                    results.failed++;
                }

                results.details.push({ jobId: jobId, status: status });
            } catch (jobError) {
                results.failed++;
                results.details.push({ jobId: jobId, status: "Error", error: Stringify(jobError) });
            }
        }
    }

    Write(Stringify(results));
} catch (e) {
    Write(Stringify({ success: false, message: "Unexpected error.", error: Stringify(e) }));
}
</script>Code language: HTML, XML (xml)

This is the kind of script you hope to never need, and that’s exactly why it’s worth building and testing before an actual incident, not during one. A tested, one-click bulk-cancel automation turns “we need to stop dozens of sends right now” from a frantic manual scramble into a single automation run.

Similar Posts