Zoho CRMのSandbox(サンドボックス)とは、本番と同じ設定を写した検証用の環境です。項目や関数、ワークフローを本番に影響させずに作り込み、確かめたものを本番へ反映できます。

便利な機能ですが、「本番へ反映する日」に事故が起きやすい機能でもあります。当社は、中小の製造業(個別受注生産)の支援で、2つの会社がそれぞれ持つZoho CRMへ、サンドボックスで作った外部システム連携の設定を反映する手順を決め、片方の会社のマスタデータをもう片方の本番へAPIで移しました。

この記事では、設定の反映について事前に決めた操作順と、マスタ移行で実際に守ったルールを手順にしてまとめます。テーマは「設定の反映側」です。

サンドボックスのデータを本番へ入れる前に、重複や参照先の衝突をどう洗い出すかは、「Zohoサンドボックスのデータを本番へ入れる前の衝突チェック手順」で扱います。あわせて読むと、反映の前後で確かめることが一通りそろいます。

落とし穴1:変更一覧の一括適用に、組織変数の「値」が混ざる

サンドボックスの反映は、次の順で画面を進めます。

  1. 設定 → データ管理 → サンドボックス → 対象のサンドボックス
  2. 変更内容の一覧を開く
  3. 反映する変更を選ぶ
  4. 「変更を本番環境に適用する」→ 整合確認 → 続行

当社の案件では、このとき変更一覧に関数・項目・レイアウトだけでなく、組織変数も並びました。組織変数とは、関数から共通で読む設定値の置き場です(設定 → 開発者向け情報 → 変数)。連携先のURL、APIキー、送信モード、検証中かどうかのフラグなどを入れておくと、関数のコードを環境ごとに書き換えずに済みます。

当社の案件では、反映元の変更一覧に、次のような変数が含まれていました。

  • 検証中かどうかを示すフラグ
  • 外部システムの接続先URLと認証キー
  • 外部システムへの送り方を切り替える値
  • 連携先の仕入先レコードのID
  • 通知メールのBCC宛先
  • 会社名・メール署名・ロゴの切り替え値

どれも、サンドボックスでは検証用の値が入っています。一括で適用すると、本番の接続先が検証環境に変わったり、検証フラグが立ったり、本番の通知宛先が上書きされたりします。サンドボックスのレコードIDを入れた変数は、本番には存在しないIDを指すことになります。

対処:変数は一括適用から外し、本番の値を1件ずつ設定する

当社では、反映対象を次のように分けることにしました。

対象 扱い
関数・項目・レイアウト・自動処理 対象を選んで標準の画面から適用する。削除される項目と関連項目を事前に確認する
組織変数 一括適用から外す。本番で必要なものだけ、本番の値で個別に登録する
検証フラグ 本番は「本番」の値を維持する。サンドボックスの値を適用しない
接続先・認証キー 検証用の値を移さない。本番の値と、有効にするタイミングを確認する
レコードIDを持つ変数 本番の該当レコードを探してIDを設定する。サンドボックスのIDを流用しない
通知の宛先 本番の既存の宛先を維持する
会社ごとに違う値 組織ごとに正しい値を設定する。片方の会社の値をもう片方へ複写しない
外部連携(接続) 認可先が正しい本番の組織かを確認し、必要なら認可し直す

変数を外したことで、整合確認で依存関係のエラーが出ることがあります。そのときも、エラーを消すために検証用の値をそのまま適用してはいけません。必要な変数を本番の値で先に登録し、もう一度整合確認を通します。

設定したあとは、変数の値を画面で読み戻して確かめます。特に、外部システムへの送信を有効にする値は、受け手側の準備が終わってから設定し、設定後にもう一度値を確認します。

落とし穴2:組織が2つあるとき、どちらから反映するか

グループ会社のように本番の組織が2つあり、互いにデータを送り合う場合は、どちらから反映するかで事故の起き方が変わります。当社では、反映の前に次の順を決めました。

  1. 対象・既存の設定・削除前の退避を確認する。スケジュールで動く送信など、利用者が操作しなくても動く処理を洗い出す
  2. 受け手側の組織を先に反映する(組織変数は一括適用から外す)
  3. 受け手側の変数・接続を本番の値で整える
  4. 送り手側の組織を反映する(同じく変数は外す)
  5. 送り手から受け手への接続先が本番の組織になっていることを確かめてから、送信を有効にする値を設定する
  6. マスタデータを移す(次の章)
  7. その日に終えた範囲と、翌日以降に有効化・確認する事項を記録する

「利用者が操作しないから大丈夫」に頼らないことが大事です。反映した瞬間から、スケジュールやワークフローは動き出します。

落とし穴3:マスタ投入でワークフローと外部送信が動く

設定を反映したあと、マスタデータ(取引先・仕入先・商品など)を本番へ入れることがあります。当社の案件では、片方の会社の本番から6種類のマスタを取り出し、もう片方の本番へレコードAPIで直接登録しました。

ここで怖いのは、登録したレコードに対して、反映したばかりのワークフローが動くことです。今回の設定には、マスタが作られたら外部の生産管理システムへ送る関数が入っていました。移行のための登録で、その送信が一斉に走ってしまいます。

APIでワークフローを起動させないには:“trigger”: [] を付ける

Zoho CRMのレコード作成・更新APIには trigger というキーがあります。公式ドキュメント(Insert Records)では、次のように案内されています。

  • 指定できる値は workflow、approval、blueprint、pathfinder、orchestration
  • trigger を指定しない場合、そのAPIに関係する自動処理が実行される
  • 空配列 [] を指定すると、ワークフローを実行しない

つまり、キーを送らなければ動き、空配列を送れば動かない、という関係です。当社の案件でも、外部システムから trigger キーを付けずに更新したときは、ワークフローが動くことを確認していました。

リクエストの本文は次の形です。trigger は data と同じ階層(本文の最上位)に置きます。

{
  "data": [
    {
      "Account_Name": "サンプル商事株式会社",
      "Phone": "000-0000-0000",
      "Source_Key": "SRC-0001"
    }
  ],
  "trigger": []
}

1回のAPI呼び出しで作成できるのは100件までです(公式ドキュメント)。件数が多い場合は分けて送ります。

スクリプトで送る場合は、送信の直前に「本文の最上位に trigger があり、空配列であること」を検査してから送ると安全です。当社の案件では、作成・更新のすべてのリクエストを検査し、送った後にも全件で確かめました。

// 送信前に trigger の空配列を必ず検査する
function assertNoTrigger(body) {
  if (!Array.isArray(body.trigger) || body.trigger.length !== 0) {
    throw new Error("trigger が空配列になっていません。送信を中止します");
  }
}

async function createRecords(apiDomain, token, module, records) {
  const body = { data: records, trigger: [] };
  assertNoTrigger(body);
  const res = await fetch(`${apiDomain}/crm/v8/${module}`, {
    method: "POST",
    headers: {
      Authorization: `Zoho-oauthtoken ${token}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });
  return res.json();
}

アクセストークンの用意は「Zoho APIをスクリプトから叩く最短ルート|Self Client / Client Credentials設定」で解説しています。

移行のためにワークフロー全体を無効にしたり、連携先の認証キーを消したり、送信する関数を書き換えたりはしませんでした。本番では、移行と同時に通常業務のワークフローも動いている必要があるからです。trigger の空配列なら、移行のリクエストだけを対象にできます。

本番へのマスタ投入はどの順番で進めるか

本番へマスタを入れるとき、書き込まずに試し、参照される側から順に一件ずつ登録して確かめ、問題がなければ全量を入れて照合する順番を示した図

当社がマスタ移行で実際に使った手順です。

  1. 送り元の本番からマスタを取得する
  2. 項目の対応と参照IDの変換を、書き込みなしで試す(ドライラン)。書き込めない項目、変換できない選択肢の値、参照先が見つからないものを0件にする
  3. 参照される側のマスタから順に登録する(例:分類 → 材質 → 仕入先 → 取引先 → 商品)
  4. まず各マスタから1件ずつ登録し、保存後のデータを取得して中身を確かめる
  5. その時刻以降に、関数の実行記録や失敗一覧に新しい記録がないかを画面で確かめる
  6. 問題がなければ残りを全量登録する
  7. 全件を取得し直し、件数・重複・参照関係を対応表と照合する

旧ID→新IDの対応表は、応答ごとに記録する

参照項目(ルックアップ)には、移行先のレコードIDを入れる必要があります。そのため、送り元のIDと、登録で返ってきた新しいIDの対応表を作ります。

このとき、作成した順番から対応を推測してはいけません。当社の案件では、分類マスタに同じ名前のレコードが2件ありました。名前で後から突き合わせると、どちらがどちらか決められません。1件登録するごとに、その応答で返ったIDを送り元のIDと並べて記録しました。

必須項目が欠けているレコードは、推測で埋めない

全量登録では、送り元で入力途中だった商品が、移行先の必須項目を満たさずに登録できませんでした。ここで値を推測して埋めたり、ダミーで登録したりはしていません。一覧にしてお客様に確認し、入力が終わってから登録する扱いにしました。そのレコードを参照する別のマスタも、参照の設定を保留にしています。

ワークフローが動かなかったことはどう確かめるか

当社が確認した時点では、API v8の GET /settings/workflow_rules は両方の組織で INVALID_REQUEST_METHOD を返しました。APIだけでは「ワークフローが動かなかった」ことを確かめきれません。少数登録の直後に、画面で関数の実行記録と失敗一覧を見る手順を、作業の中に組み込んでおきます。

それでも、外部システム側に何も届いていないことの完全な証明にはなりません。受け手側の受信記録も、可能なら合わせて確認します。

反映当日のチェックリスト

  1. 変更一覧から、反映する設定だけを選んだか
  2. 組織変数を一括適用から外したか
  3. 本番に必要な変数を、本番の値で個別に登録し、読み戻したか
  4. 接続の認可先が、正しい本番の組織になっているか
  5. 外部への送信を有効にする値は、受け手の準備が終わってから設定したか
  6. マスタ投入のリクエストすべてに "trigger": [] が入っているか
  7. 少数で登録し、保存内容とワークフローの非起動を確かめてから全量へ進んだか
  8. 旧ID→新IDの対応を、応答ごとに記録したか
  9. 全件を取得し直し、件数・重複・参照関係を照合したか
  10. 終わった範囲と、翌日以降に残した作業を記録したか

まとめ

サンドボックスの反映で守ることは、2つに絞れます。設定は反映しても、検証用の値は持ち込まない。データは入れても、自動処理は起こさない。前者は組織変数を一括適用から外すこと、後者は "trigger": [] を付けることで実現できます。

どちらも、作業の前に「何が動き出すか」を洗い出しておけば防げる事故です。反映の日は、手順書とチェックリストを用意してから始めることをおすすめします。

なお、関数のコードにサンドボックスのレコードIDが残ったまま本番へ移ると、APIがエラーを返し続けることがあります。その見つけ方はワークフローが「成功」なのに動かない事例で紹介しています。サンドボックスを使った設定変更や外部システム連携の進め方を相談したい場合は、販売管理システムとCRMの統合構築サービスのページをご覧ください。

よくある質問

組織変数をサンドボックスから本番へ移してはいけないのですか?

変数の「定義」が必要なこと自体は問題ありません。問題は「値」です。サンドボックスで検証用の接続先やキーを入れていると、一括適用でその値が本番に入ります。本番に必要な変数だけを本番の値で個別に登録し、依存関係のエラーが出たら、検証値を流し込まずに本番の値を先に用意して解消します。

"trigger" キーを送らなかった場合はどうなりますか?

Zohoの公式ドキュメントでは、trigger を指定しないと、そのAPIに関係する自動処理が実行されると案内されています。当社の案件でも、外部システムから trigger キーを付けずに更新したところ、ワークフローが動くことを確認しています。止めたいときは空配列を明示します。

ワークフローを一時的に無効にしてから移行するのではだめですか?

本番では、移行中に通常の業務データで動くべきワークフローまで止まってしまいます。戻し忘れのリスクもあります。trigger の空配列なら、移行のリクエストだけを対象にできます。

APIでワークフローの実行履歴を取れますか?

当社が確認した時点では、API v8の GET /settings/workflow_rules は INVALID_REQUEST_METHOD を返し、実行履歴は取れませんでした。少数投入のあとに、画面で関数の実行記録や失敗一覧に新しい記録がないかを確かめる手順にしています。