外部サービス連携

条件付きでレコード作成をSlackへ通知するApexとフロー

Incoming WebhookのURLを取得し、Queueableで通知を送るApexクラスを作成し、レコードトリガーフローから呼び出すところまでを説明します。

2023.03.15

シナリオ

取引先レコードが作成されたとき、取引先名に「テスト」という文字列が含まれる場合だけ、Slackへ通知します。すべての作成を通知するわけではありません。条件を満たしたときだけ、という判定が必要です。

先に確かめること 標準のSlack連携で足りないか

SalesforceにはSlackと連携する標準機能があります。チャンネルとレコードを結び付けて、レコードの更新をSlackへ流すような使い方が用意されています。まずは、その標準機能で要件が満たせないかを確かめてください。

本記事で扱うのは、「取引先名に特定の文字列を含む場合だけ」のように、条件で絞り込んでから通知するケースです。ここまで細かい条件分岐は自作のApexとフローが必要になります。

Incoming Webhookを発行する

SlackのIncoming Webhookは、投稿先チャンネルが決まったURLに対してJSONをPOSTするだけでメッセージを送れる仕組みです。現在はSlack Appを作成したうえで、その機能として発行します。

  1. Slack Appを作成する

    Slackの管理画面でアプリを新規作成し、投稿先のワークスペースを選びます。

  2. Incoming Webhooksを有効にする

    アプリの設定で「Incoming Webhooks」機能をオンにします。

  3. ワークスペースへWebhookを追加する

    「Add New Webhook to Workspace」から投稿先チャンネルを選び、認可します。

  4. 発行されたURLを控える

    チャンネルごとに1本のURLが発行されます。このURLを知っている人は誰でもそのチャンネルへ投稿できるため、扱いには注意します。

Slackの投稿先チャンネルを選ぶ画面。赤枠に選択欄と追加ボタンがある
投稿先チャンネルを選ぶ画面です。赤枠のプルダウンでチャンネルを選び、下のボタンで追加します。
発行されたWebhook URLの画面。URLの大部分は黒く塗りつぶされている
発行されたWebhook URLです。赤枠のこの一本が投稿の鍵になるので、第三者に渡さないでください。
Slackのチャンネルにインテグレーションが追加された旨の1行表示
追加が済むと、チャンネルにこの行が出ます。連携が入ったことの目印として見てください。

発行されるURLは次の形です。

https://hooks.slack.com/services/TXXXXXXXXXX/BXXXXXXXXXX/xxxxxxxxxxxxxxxxxxxxxxxx

実際にはチャンネルごとに異なる文字列が入ります。第三者に渡さないでください。

Webhook URLをApexに直書きしない

⚠️ Webhook URLはSlackに投稿できる鍵そのものです。Apexクラスの中に文字列リテラルで埋め込むと、そのクラスを見られるすべての人に渡ることになり、変更のたびにデプロイも必要になります。

コードに出さない置き場所として、次のような選択肢があります。

置き場所向いている場面
カスタムメタデータ型値を環境ごとに変え、デプロイでも配布したい場合
カスタム設定(階層型)管理者が画面から値を書き換えられるようにしたい場合
名前付き認証情報エンドポイントをコードから完全に切り離し、リモートサイト設定も省きたい場合

ここではカスタムメタデータ型を使います。SlackWebhook__mdt を作成し、Url__c(テキスト)に発行したURLを保存します。

通知用Apexクラスを作成する

Webhook URLはメタデータ型から読み込みます。呼び出し元にはInvocableVariableでメッセージとチャンネルを渡せるようにします。コールアウトはトリガー由来の同期処理から直接は行えないため、Queueableに包みます。

public with sharing class PublishToSlack {
    public class SlackRequest {
        @InvocableVariable(label='Slack Message' required=true)
        public String slackMessage;
        @InvocableVariable(label='Channel or User' required=true)
        public String channel;
    }

    @InvocableMethod(label='Publish to Slack' callout=true)
    public static void publish(List<SlackRequest> requests) {
        SlackWebhook__mdt setting = SlackWebhook__mdt.getInstance('Default');
        String webhookUrl = setting.Url__c;
        for (SlackRequest r : requests) {
            JSONGenerator gen = JSON.createGenerator(true);
            gen.writeStartObject();
            gen.writeStringField('text', r.slackMessage);
            gen.writeStringField('channel', r.channel);
            gen.writeStringField('username', 'Salesforce Bot');
            gen.writeEndObject();
            System.enqueueJob(new NotifySlackJob(webhookUrl, gen.getAsString()));
        }
    }
}

フローから渡された内容をQueueableへ渡し、コールアウトを非同期で実行します。読み取りはメタデータ型のみで、DMLは行いません。

public class NotifySlackJob implements System.Queueable, Database.AllowsCallouts {
    private final String endpoint;
    private final String body;

    public NotifySlackJob(String endpoint, String body) {
        this.endpoint = endpoint;
        this.body = body;
    }

    public void execute(System.QueueableContext ctx) {
        HttpRequest req = new HttpRequest();
        req.setEndpoint(endpoint);
        req.setMethod('POST');
        req.setHeader('Content-Type', 'application/json');
        req.setBody(body);
        Http http = new Http();
        HttpResponse res = http.send(req);
        System.debug(LoggingLevel.INFO, 'Slack response: ' + res.getStatusCode());
    }
}

Queueableの中でHTTPのPOSTを実行します。Database.AllowsCalloutsが無いと実行時エラーになります。

@InvocableMethod は、フローから呼び出せるメソッドを1クラスにつき1つだけ指定するアノテーションです。callout 修飾子は、そのメソッドが外部システムへコールアウトすることを示します。外部への通信を伴うURLをリモートサイト設定へ登録する必要がありますが、名前付き認証情報を使う場合はリモートサイト設定の登録が不要になります。

レコードトリガーフローを作成する

⚠️ この記事を最初に書いた当時は、プロセスビルダーで組む前提でした。プロセスビルダーとワークフロールールはすでにサポートが終了しており、新規の自動化はフローで作成します。

  1. フローの種類を選ぶ

    Flow Builderで新規作成し、「レコードトリガーフロー」を選びます。対象オブジェクトは取引先、条件は「レコードが作成されたとき」にします。

  2. 条件を設定する

    「フローの実行条件」に、取引先名に「テスト」を含む場合という条件式を追加します。

  3. Apexアクションを配置する

    要素を追加し、Apexアクションから「Publish to Slack」を選びます。

  4. 入力値を設定する

    Slack Messageに通知文、Channelに投稿先チャンネル名を設定します。値はレコードの項目を式で組み立てられます。

  5. 保存して有効化する

    フローに名前を付けて保存し、有効化します。

動作確認

取引先名を「テスト顧客」としたレコードを新規作成し、保存します。フローの実行条件に一致するため、Apexアクションが呼び出され、Queueableのジョブがコールアウトを実行します。Slackの投稿先チャンネルに、設定したメッセージが届いていれば成功です。届かない場合は、Setup の「フローの実行結果」と、Apexジョブの実行履歴(非同期Apexジョブ)を確認します。

取引先の新規作成画面。取引先名の欄にテスト顧客と入力されている
動作確認用の取引先です。赤枠の取引先名に「テスト」を含めて保存してください。
SlackにSalesforce Botとして投稿されたメッセージ
届いた通知です。赤枠の本文に、作成した取引先名が入っていれば成功です。

ここで間違えやすい

間違い何が起きるか
Webhook URLをApexへ直書きする動きはしますが、値の変更にデプロイが要り、コードを見た人にURLが渡ります
Queueableクラスに Database.AllowsCallouts を付けない実行時に例外になります
プロセスビルダーで新規に組もうとするサポートが終了しているため避けます。レコードトリガーフローを使います

確認した環境

  • 2026年9月 / Salesforce Summer '26(API バージョン 67.0)時点の公式ドキュメントで確認しています
  • Slack側の手順は docs.slack.dev(api.slack.com からのリダイレクト先)の公式ドキュメントで確認しています

まとめ

  • Webhook URLはApexへ直書きせず、カスタムメタデータ型・カスタム設定・名前付き認証情報のいずれかに置きます
  • コールアウトは @future(callout=true) かQueueableで非同期に行います。トリガー由来の同期処理から直接は呼べません
  • プロセスビルダーとワークフロールールはサポートが終了しています。新規の自動化はレコードトリガーフロー+@InvocableMethod で組みます
  • 条件で絞り込む通知は自作が要りますが、まず標準のSlack連携で足りないかを確かめる価値があります

参考:当時の画面

記事を最初に書いた当時の画面です。いまの手順と違うところは、各画像の説明に書いています。

オブジェクトに取引先、開始条件にレコードを作成したときのみを選んだ設定画面
起動のタイミングを決める画面です。赤枠の「レコードを作成したときのみ」を選びます。
条件設定画面。項目がAccount.Name、演算子が次の文字列を含む、値がテスト
絞り込み条件です。赤枠の3つで「取引先名にテストを含む」を組み立てます。
Apexをコールの設定画面。クラス名と、チャンネルとメッセージの2変数に値が入っている
Apexを呼ぶ設定です。赤枠のクラス名と、チャンネル・メッセージへ渡す値を見てください。
取引先を起点に条件で分岐し、TRUEのときだけPublish to Slackを呼ぶ全体図
組み上がった全体の流れです。条件がTRUEのときだけApexが呼ばれる点を見てください。

Salesforceの導入・運用についてご相談ください

導入前の検討から、お使いの環境の改修・運用、AIとの連携まで承ります。状況を伺ったうえで、進め方をご提案します。