【第450回】 Marketing Cloud Next : API を使ってフローから離脱させる方法
Marketing Cloud Next Growth & Advanced Edition では、セグメントトリガーフロー、イベントトリガーフロー、オンデマンドフロー など、さまざまなフローを使ってリアルタイム施策や自動化を実現できます。
しかし運用していると、こんなケースはありませんか?
✅ 購入完了したのにリマインドメールが送られ続ける
✅ すでに対応済みなのにフォローアップが止まらない
✅ 外部システム側で処理完了したためフローを終了したい
このような 「もうフローに残す必要がない個人」 を動的に離脱させるための仕組みが、Spring '26 の新機能リリースで登場した Exit Individuals from a Flow Action(REST API)です。
この API を使うと、
実行中のフローから特定の個人(Individual)を即座に退出させる
ことができます。
ユースケース例
🛒 購入リマインドフロー
カート放棄
1 日後リマインド
3 日後再通知
7 日後クーポン送付
途中で、外部 EC サイトで購入完了
このままだと
❌ 「まだ購入していませんか?」メールが送られる
⇒ そこで、Exit Individuals API でフローから離脱させる
対応フロータイプ
このアクションは、以下のフローで利用可能です。
セグメントトリガーフロー
イベントトリガーフロー
オンデマンドフロー
API仕様
この API は「API version 66.0」(Spring '26)以降で動きます。
--- メソッド
POST
--- エンドポイント
https://[私のドメイン名].my.salesforce.com/services/data/v66.0/actions/custom/exitIndividualsFromFlow/flow_API_name
--- ヘッダー
Content-Type:application/json
Authorization:Bearer [アクセストークン]
--- ボディ
{
"inputs": [
{
"individualId": "003R000000ExAmPlE",
"flowVersionId": "301R000000AbCdEfG",
"description": "Customer completed purchase via external portal."
}
]
}フローバージョン(flowVersionId)は任意です。指定がない場合は、すべてのバージョンから退出します。
説明(description)も任意です。


実装イメージ(活用パターン)
パターン①:外部EC完了トリガー
EC → Webhook → Lambda → REST API → Exit Flow
パターン②:CRM更新トリガー
Opportunity Closed Won → Apex → Callout → Exit Flow
パターン③:サポート完了
Case Closed → Event-Triggered Flow → Exit Flow
取引先責任者のページに強制退出画面を作る
最後に、画面フローを作成して連絡先の退出画面を作成してみます。
具体的には、以下のような流れになります。
Contact レコード
↓(クイックアクション)
Screen Flow
↓
Contact → IndividualId 取得
↓
Flow API 名を手入力
↓
Apex(Invocable)
↓
POST /actions/custom/exitIndividualsFromFlow/{flowApiName}

設定手順
1. Named Credential の作成
この作成方法については、以下の 2 つの記事をご確認ください。
以下の記事で「コンシューマー鍵」と「コンシューマーの秘密」を取得するところまで進めてください。
上で取得した「コンシューマー鍵」と「コンシューマーの秘密」を使って、Named Credential(指定ログイン情報)を作成してください。
「データグラフのスケジュール実行」セクションの 手順 1 ~ 24 を実行してください。下の Apex をそのまま利用する場合は、Named Credential の Name(API 名)を「SelfOrg」と命名してください。
2. Apex クラス を登録
以下を登録してください。
設定で登録されている Named Credential(指定ログイン情報)の Name(API 名)が「SelfOrg」であるか、再度確認してください。
public with sharing class ExitIndividualsFromFlowAction {
public class Request {
@InvocableVariable(required=true)
public String individualId;
@InvocableVariable(required=true)
public String flowApiName;
}
public class Result {
@InvocableVariable public Boolean success;
@InvocableVariable public Integer statusCode;
@InvocableVariable public String message;
@InvocableVariable public String responseBody;
}
@InvocableMethod(label='Exit Individual From Flow')
public static List<Result> run(List<Request> requests) {
List<Result> results = new List<Result>();
for (Request req : requests) {
Result r = new Result();
try {
String apiName = (req.flowApiName == null) ? '' : req.flowApiName.trim();
HttpRequest httpReq = new HttpRequest();
httpReq.setMethod('POST');
httpReq.setEndpoint(
'callout:SelfOrg/services/data/v66.0/actions/custom/exitIndividualsFromFlow/' +
EncodingUtil.urlEncode(apiName, 'UTF-8')
);
httpReq.setHeader('Content-Type', 'application/json');
httpReq.setTimeout(120000);
Map<String,Object> body = new Map<String,Object>{
'inputs' => new List<Object>{
new Map<String,Object>{ 'individualId' => req.individualId }
}
};
httpReq.setBody(JSON.serialize(body));
HttpResponse res = new Http().send(httpReq);
r.statusCode = res.getStatusCode();
r.responseBody = res.getBody();
r.success = (r.statusCode >= 200 && r.statusCode < 300);
if (r.success) {
r.message = 'Successfully exited';
} else {
r.message = r.responseBody;
if (r.statusCode == 400 && !String.isBlank(r.responseBody)) {
String errCode = extractFirstErrorStatusCode(r.responseBody);
if (errCode == 'INDIVIDUAL_NOT_IN_FLOW') {
r.message = 'This individual is not currently in the flow (no exit required)';
}
}
}
} catch (Exception e) {
r.success = false;
r.message = 'Exception: ' + e.getMessage();
}
results.add(r);
}
return results;
}
private static String extractFirstErrorStatusCode(String jsonText) {
try {
Object root = JSON.deserializeUntyped(jsonText);
if (!(root instanceof List<Object>)) return null;
List<Object> rootList = (List<Object>)root;
if (rootList.isEmpty()) return null;
Object first = rootList[0];
if (!(first instanceof Map<String,Object>)) return null;
Map<String,Object> firstMap = (Map<String,Object>)first;
Object errorsObj = firstMap.get('errors');
if (!(errorsObj instanceof List<Object>)) return null;
List<Object> errors = (List<Object>)errorsObj;
if (errors.isEmpty()) return null;
Object e0 = errors[0];
if (!(e0 instanceof Map<String,Object>)) return null;
Map<String,Object> e0Map = (Map<String,Object>)e0;
Object statusCodeObj = e0Map.get('statusCode');
return (statusCodeObj == null) ? null : String.valueOf(statusCodeObj);
} catch (Exception ignore) {
return null;
}
}
}
3. 画面フローの作成
1. 新規で「画面フロー」を作成します。

2. 今回、以下の 5 つの「変数」を作ります。
(1)recordId
・API Name:recordId
・Data Type:Text
・Available for input:ON
(2)varIndividualId
・API Name:varIndividualId
・Data Type:Text
(3)varMessage
・API Name:varMessage
・Data Type:Text
(4)varStatusCode
・API Name:varStatusCode
・Data Type:Number
・Decimal Places:0
(5)varSuccess
・API Name:varSuccess
・Data Type:Boolean

3. 「レコードを取得」要素を設定します。
表示名:Get Contact
API 名:Get_Contact
Object:Contact
Condition Requirements:All Conditions Are Met (AND)
Field:Contact Id
Operator:Equals
Value:{!recordId}(変数)
How Many Records to Store:Only the first record(デフォルト)
How to Store Record Data:Automatically store all fields(デフォルト)

4. 「割り当て」を設定します。
表示名:Set IndividualId
API 名:Set_IndividualId
Set Variable Values:varIndividualId(変数) = {!Get_Contact.Id}

5. 「画面」要素を配置して、以下を入力します。
表示名:Input Flow API Name
API 名:Input_Flow_API_Name

6. 「テキスト」コンポーネントをドラッグアンドドロップして、以下を入力して、要素を保存します。
表示名:Flow API Name
API 名:varFlowApiName(← 入力されたフローの API 名が、この変数に格納されます。)
必須:ON

4. Apex アクション
1. Action を配置して、作成した Apex(ExitIndividualsFromFlowAction)を選択します。

2. 表示名と API 名を決め、以下の 2 つをマッピングします。
表示名:Exit Individual From Flow
API 名:Exit_Individual_From_Flow
Set Input Values:
flowApiName = {!varFlowApiName}(画面上からの変数)
individualId = {!varIndividualId}(変数)

一旦、ここまでで実装は成立していますが、以下に結果を表示するための画面を作成します。
5. 出力応答の割り当て
最初に作成した残り 3 つの変数に、Apex からの出力応答 を割り当てます。
「割り当て」要素を配置してください。
表示名:Set Result
API 名:Set_Result
Set Variable Values:
varSuccess = {!Exit_Individual_From_Flow.success}
varStatusCode = {!Exit_Individual_From_Flow.statusCode}
varMessage = {!Exit_Individual_From_Flow.message}

6. 成功と失敗の分岐の作成
「決定」要素を配置して
表示名:Check Success
API 名:Check_Success
左のパス名:Success
右のパス名:Failure
左の条件:{!Exit_Individual_From_Flow.success} = True

7. 成功画面の作成
左のパスに「画面」要素を配置して、設定画面を表示したら、表示名と API 名を設定します。
表示名:Success Exit Completed
API 名:Success_Exit_Completed

続いて、Display Text を配置して、以下を入力して「保存」します。
API 名:Success_Display
テキスト本文:
✅ Exit successfulStatus: {!varStatusCode}

8. 失敗画面の作成
右のパスにも「画面」要素を配置して、以下を入力します。
表示名:Error Exit Flow
API 名:Error_Exit_Flow

こちらも、Display Text を配置して、以下を入力して「保存」します。
API 名:Failure_Display
テキスト本文:
❌ Exit failedStatus: {!varStatusCode}
Details: {!varMessage}

9. フローの保存と有効化
フローを保存して有効化してください。
フロー名:Exit Individual From Flow
API 名:Exit_Individual_From_Flow

10. 取引先責任者のアクションに設定
1. 設定 > オブジェクトマネージャーから「取引先責任者」の「Buttons, Links, and Actions」を選択して「新しいアクション」をクリックします。

2. 以下で入力します。
アクションタイプ:Flow
フロー:Exit Individual From Flow
表示名:Exit Flow
API 名:Exit_Flow

3. 新しいアクションの保存後、取引先責任者の「ページレイアウト」に追加します。

11. 動作確認
1. フロー内に存在している「取引先責任者」のレコードを開いて、Exit Flow クイックアクションをクリックします。

2. 「Exit Flow」の画面が開きますので、Flow API 名(❌ 表示名)を入力して先に進みます。(例:オンデマンドフロー の Order_Confirmation_Flow)

3. 「退出されました」と表示されます。

4. その後のフローでは、先に進まなくなります。成功です。

5. フロー内に存在していない「取引先責任者」レコードの場合は、以下の通り「退出されませんでした」が表示されます。

いかがでしたでしょうか。
Marketing Cloud Next では、「開始する」だけでなく 「途中で止める」制御 も非常に重要です。
Exit Individuals API は、Flow をよりインテリジェントに運用するための必須テクニック と言えます。
リアルタイム施策や外部連携をしている方は、ぜひ活用してみてください。
今回は以上です。
