API

ApexからfaximoSilverのFAX送信APIを呼ぶ実装

FAX送信に必要な項目をApexで組み立て、REST APIへリクエストを送って結果を確認します。認証情報を直書きしない方法と、DMLの後に呼べない制限もあわせて説明します。

2024.03.11

なぜApexからFAX送信を呼ぶのか

取引先によっては、いまもFAXでのやり取りが残っています。Salesforce上のレコード操作をきっかけに帳票をFAX送信できれば、送り忘れや宛先の打ち間違いを減らせます。

faximoSilver(株式会社エディックワークスが提供するクラウドFAXサービス)は、Web API(REST・SOAP)でのシステム連携に対応しており、ApexのHttpRequestから直接呼び出せます。⚠️ エンドポイントやパラメータの詳細は、契約時に配布される非公開の仕様書に基づくものです。

認証とリクエストの形式

認証はBasic認証です。ユーザー名とパスワードをコロンでつなぎ、Base64エンコードした値を、標準のAuthorizationヘッダーではなく独自のX-Authヘッダーに載せます。送信先やメッセージ本文はJSONのボディに入れ、POSTで送ります。

プログラム

public class FaxSendService {
    public class FaxSendException extends Exception {}

    @future(callout=true)
    public static void sendFax(String faxNo) {
        String username = 'xxxxxxxx';
        String password = 'xxxxxxxx';
        String resaddress = 'xxxxxxxx@example.com';
        String userkey = 'xxxxxxxx';
        String tsi = '12345678901';
        String headerinfo = '@d@ @t@ to:@T@ @p@';
        String retrynum = '2';
        String subject = 'Fax Api テスト用(添付ファイルなし版)';
        String bodyText = '';

        HttpRequest req = new HttpRequest();
        req.setMethod('POST');
        req.setEndpoint('https://rest.faximo.jp/snd/v2/request.json');
        req.setTimeout(120000);

        Blob headerValue = Blob.valueOf(username + ':' + password);
        String idBase64Encoded = EncodingUtil.base64Encode(headerValue);
        req.setHeader('Content-Type', 'application/json');
        req.setHeader('X-Auth', idBase64Encoded);
        req.setHeader('X-Processkey', 'hogehogehoge1234567890');

        Map<String, Object> payload = new Map<String, Object>{
            'sendto' => new List<Object>{ new Map<String, Object>{ 'faxno' => faxNo } },
            'userkey' => userkey,
            'tsi' => tsi,
            'headerinfo' => headerinfo,
            'retrynum' => retrynum,
            'resaddress' => resaddress,
            'subject' => subject,
            'body' => bodyText
        };
        req.setBody(JSON.serialize(payload));

        Http http = new Http();
        HttpResponse res = http.send(req);

        if (res.getStatusCode() == 200) {
            Map<String, Object> resBody = (Map<String, Object>) JSON.deserializeUntyped(res.getBody());
            Object idxcnt = resBody.get('idxcnt');
            if (idxcnt == null) {
                throw new FaxSendException('idxcnt error');
            }
        } else {
            throw new FaxSendException('faximo API status: ' + res.getStatusCode());
        }
    }
}

faximoSilverのFAX送信APIをコールアウトし、送信結果を確認します。@future(callout=true)を付け、DMLの直後からでも呼べるようにしています。

Base64エンコードにはEncodingUtil.base64Encode(headerValue)を使います。JSONボディは文字列連結ではなくJSON.serialize()で組み立て、失敗したときは戻り値で知らせるのではなく例外を投げる作りにしています。

ユーザー名・パスワードは、Apexのクラスに直接書きません。名前付き認証情報にfaximoSilverのエンドポイントを登録し、外部資格情報にユーザー名・パスワードを設定すれば、Apexからは認証情報が直接見えなくなります。カスタムメタデータ型は、tsiやheaderinfoのような秘密ではない送信設定を、環境ごとに切り替えたいときに向いています。

DMLの後にそのまま呼べない

Apexのコールアウトには制限があります。同じトランザクションの中で、DML文(レコードの登録・更新・削除)を実行した後にコールアウトを呼ぶと、エラーになります。「未確定の処理があります。コールアウトの前にコミットまたはロールバックしてください」という趣旨のメッセージが出ます。

たとえば「FAX送信済みフラグを立ててから送信する」という順番の処理を、DMLとコールアウトを同じメソッドの中に書くと失敗します。@future(callout=true)を付けた非同期メソッドに分けると、DMLが確定した後の別トランザクションでコールアウトが実行されるため、この制限に当たりません。上のコードも、この理由で@future(callout=true)を付けています。

⚠️ future メソッドの引数に使えるのは、プリミティブ型とその配列・コレクションだけです。レコード(sObject)はそのまま渡せないため、IDを渡してメソッドの中で取り直します。

動作確認

  1. 開発者コンソールを開く

    「実行」から匿名実行ウィンドウを開きます。

  2. 送信先のFAX番号を指定して呼び出す

    FaxSendService.sendFax('123456789');を実行します。future メソッドなので、実行後にトランザクションが確定してから送信されます。

  3. 結果を確認する

    ステータスコード200かつidxcntが返っていれば受付成功です。faximoSilverの管理画面で、送信済みFAXの一覧に記録が残っていることを確認します。

実行ログの1行。USER_DEBUGでres.getStatusCode():200と出力されている
実行ログです。ステータスコードが200で返っているかを、ここで確認してください。
受信したFAXの紙面。件名のFax Api テスト用(添付ファイルなし版)だけが印字されている
実際に届いたFAXです。Apexで指定した件名がそのまま紙面に印字されることを見てください。

ここで間違えやすい

間違い何が起きるか
DMLの直後、同じメソッドの中でコールアウトを呼ぶ「未確定の処理があります」というエラーになります
ステータスコード200だけを見て成功と判断するidxcntが無い応答もあるため、あわせて確認します
ユーザー名・パスワードをApexのクラスに直書きするソースコードを見られる人全員に認証情報が渡ります
futureメソッドにsObjectをそのまま渡そうとするコンパイルが通りません。IDを渡してメソッド内で取り直します

確認した環境

  • 2026年9月、help.salesforce.com(DML後のコールアウト制限)とdeveloper.salesforce.com(Future Methods)の公式ドキュメントで、制限事項と引数の条件を確認しています
  • faximoSilverは、2026年9月時点の公式サイト(edicworks.com)で、無料トライアルの申し込みと資料請求の案内が確認できました

まとめ

  • faximoSilverのFAX送信APIは、Basic認証とJSONボディのPOSTで呼び出します
  • ユーザー名・パスワードはApexに直書きせず、名前付き認証情報と外部資格情報に置きます
  • DMLの直後にコールアウトを呼ぶとエラーになります。@future(callout=true)で別トランザクションに分けます
  • futureメソッドの引数はプリミティブ型とその配列・コレクションのみで、sObjectは渡せません
  • ステータスコード200だけでなく、応答のidxcntも確認して送信結果を判断します

参考:当時の画面

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

開発者コンソールの匿名実行ウィンドウ。1行のApexコードを入力し実行ボタンが並ぶ
匿名実行ウィンドウです。Open Logにチェックを入れてから実行する点を見てください。

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

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