本文へスキップ

エンジニアリング

Shopify Flow連携の実装:トリガー、アクション、そして一周期を失った落とし穴

著者 Jahangir Alam · 2026年7月4日 · 約11分で読めます

要点: Shopify Flowの連携には三つの可動部があります。トリガー(アプリがFlowに送るイベント)、アクション(Flowがアプリを呼び出すステップ)、そしてテンプレート(出来合いのワークフロー)です。拡張機能の設定は素朴なTOMLですが、罠はプラットフォームの文書化されていない規則の中にあります。私たちが実際にデバッグの一周期を費やした二つは、トリガーのフィールドキーとアクションのフィールドキーで記法の慣習が逆であること、そしてトリガーで宣言したすべての項目が、すべてのペイロードで必須であることです。つまり「省略可能な参照フィールド」は矛盾であり、ゲストのイベントを静かに壊します。QuotWayで10個のトリガー、3個のアクション、6個のテンプレートを提供した経験から、その全体像を示します。

Flow連携とは何か(そして誰が使えるのか)

Shopify FlowはShopifyのノーコード自動化ビルダーです。アプリは三つの方法で接続します。

  • トリガーflow_trigger) - アプリがイベント(「見積が送信された」)を送り、マーチャントはそれを起点にワークフローを組み立てます。
  • アクションflow_action) - Flowのワークフローがアプリを呼び出して何かを行わせ(「見積の状況を更新する」)、マーチャントが設定した項目を渡します。
  • テンプレートflow_template) - Flowのテンプレートギャラリーに現れる、あらかじめ作られたワークフロー。マーチャントは白紙ではなく動く例から始められます。

まず一つ、よくある誤解を潰しておきます。アプリが提供する拡張機能について、FlowはPlus専用ではありません。 公開されているApp Storeアプリのトリガーとアクションは、Plusに限らずどの有料Shopifyプランのマーチャントでも利用できます。(マーチャント自身が作るFlowのワークフローには別のプラン規則がありますが、それは拡張機能が利用可能かどうかとは別の話です。)私たちが区分しているのはパッケージ上の選択によるもので、トリガーはProfessional、アクションはEnterpriseです。プラットフォームがそれを強制しているわけではありません。

三つの構成要素

トリガー、アクション、テンプレートはそれぞれ独立した拡張機能のディレクトリで、shopify.extension.toml を持ちます。共通の「Flowアプリ」オブジェクトのようなものはなく、多数の小さな拡張機能から連携を組み立てます。

トリガー:アプリ → Flow

トリガーは名前と、ワークフローで使えるデータになる項目の集合を宣言します。フィールドキーは公開されたペイロードの契約です。

# extensions/flow-quote-submitted/shopify.extension.toml
[[extensions]]
type = "flow_trigger"
name = "Quote submitted"
handle = "quote-submitted"

  [[settings.fields]]
  type = "single_line_text_field"
  key = "Quote id"            # ← 単語頭大文字、スペース区切り

  [[settings.fields]]
  type = "number_decimal"
  key = "Total amount"        # マーチャントは「Total amount >= X」の条件を作ります

  [[settings.fields]]
  type = "single_line_text_field"
  key = "Customer id"         # ゲストの場合は空文字列 — 落とし穴 #2 を参照

イベントの送信は、トリガーのハンドルと、まさにその項目名をキーとするペイロードを添えて flowTriggerReceive ミューテーションを呼び出すことで行います。私たちは状態が遷移するたびに、小さな flow/emit サービスから送信しています(送信、提案の送付、承認、再見積、辞退、期限切れ、変換、そして承認関連の三つで、合計10個です)。

アクション:Flow → アプリ

アクションは、マーチャントが入力する項目と、ステップの実行時にFlowがPOSTする runtime_url を宣言します。私たちはすべてのアクションを一つのエンドポイントに送り、ハンドルで振り分けています。

# extensions/flow-update-quote-status/shopify.extension.toml
[[extensions]]
type = "flow_action"
name = "Update quote status"
handle = "update-quote-status"
runtime_url = "/api/flow/action"     # すべてのアクションで一つのエンドポイント
validation_url = "/api/flow/validate" # 保存時の項目検証
schema = "./schema.graphql"
return_type_ref = "QuoteActionResult"

  [[settings.fields]]
  type = "single_line_text_field"
  key = "quote_id"            # ← スネークケース(トリガーとは逆!)
  required = true

  [[settings.fields]]
  type = "single_line_text_field"
  key = "target_status"       # IN_REVIEW | DECLINED | EXPIRED
  required = true

/api/flow/action のハンドラーはShopifyのHMACを検証し、アクションのハンドルで振り分け、action_run_id について冪等であり(Flowは再試行します)、return_type_ref に一致する型付きの結果を返します。私たちは三つのアクションを提供しました。状況の更新、通知の送信、担当者への割り当てです。いずれもルートではなく、サービスの入口でEnterpriseに区分しています。

テンプレート:動く出発点

flow_template の拡張機能は、.flow ファイル(ワークフローの定義)とロケールの文言をまとめたものです。マーチャントが自分で配線せずに「高額の見積が届いたらSlackに投稿する」を得られるようにします。私たちは六つ提供しました。注意点として、テンプレートはFlowチームの審査を経てからギャラリーに現れます(数営業日)。そのため、アプリの他の部分とは別の時計で進みます。

落とし穴(どれも傷跡です)

落とし穴 何が起きるか 対処
記法が逆 トリガーのフィールドキーは Title Case With Spaces(「Total amount」)、アクションのフィールドキーは snake_casequote_id)です。取り違えると項目が結び付きません。 この分かれ方を覚えてください。トリガーは単語頭大文字、アクションはスネークケースです。
トリガーの全項目が必須 宣言したすべての項目が、すべてのペイロードに存在しなければなりません。「省略可能な参照フィールド」は矛盾です。一度でも省く(たとえば顧客のないゲスト見積)と、そのイベント全体が拒否されます。 参照フィールドではなく、"" になりうる素のテキスト項目を使い、Flow側で「空でない」による分岐をマーチャントに任せてください。
設定画面は保存できない ステップに対してアプリが提供する設定画面は読み取り専用で、intent.finish({properties}) は何もしません(Shopifyのスタッフに確認済み)。独自の選択UIを作っても、選択内容を保持できません。 標準の [[settings.fields]] と、保存時に不正な値を拒否する validation_url を使ってください。
CLI定義のトリガーにライフサイクルのコールバックがない TOMLで定義したトリガーには購読・購読解除のWebhookがないため、誰が待ち受けているか分かりません。購読の記録を条件に送信すると閉じる側に失敗し、誰にもイベントが届きません。 権利のあるすべてのストアに対して送信してください。待ち受けがなければ flowTriggerReceive は単に何もしません。
.flow ファイルは自己検証する テンプレートのファイルは自身のSHA-256のダイジェストを持ち、エディターからの書き出しです。手で編集すると拒否されます。 .flow を手で編集しないでください。マーチャント固有の項目を空にしたまま、Flowのエディターから書き出し直します。preInstallNote はTOMLではなくロケールのJSONにあります。
スキーマの変更には協調したリリースが必要 トリガーの項目を変更すると、送信するペイロードはデプロイ済みのスキーマと一致していなければなりません。送信側を出さずに拡張機能をデプロイする(またはその逆)と、すべてのイベントが拒否されます。 同時にリリースしてください。shopify app deploy バックエンドの反映を歩調を合わせて行います。
型に厳密 number_decimal は文字列ではなくJSONの数値である必要があり、*_reference の項目はGIDではなく数値のレガシーIDを求めます。 送信の境界で型を変換し、参照フィールドより素の項目を優先してください(落とし穴 #2 を参照)。

最初の二つが、本番でのデバッグ時間を費やしたものです。「全項目必須」は特に厄介です。項目が欠けた一部のイベントだけが壊れるため、私たちの場合はゲスト見積(顧客IDなし)といくつかの境界的な遷移でしたが、構造的な問題だと気づくまでは断続的な不具合に見えます。

本番でのFlowのデバッグ

Flowの失敗は静かです。拒否されたペイロードはマーチャントには表示されません。境界の両側でログを取り、それを検索してください。

  • 送信側: 成功時に flow.emit.sent を、flowTriggerReceive がuserErrorsを返したときに flow.emit.user_errors を記録します(スキーマの不一致はここに現れます)。
  • アクション側: 振り分けたハンドル、action_run_id、結果を記録します。HMACの失敗と冪等性による短絡も、それぞれ一行残すべきです。

トリガーが「発火しない」ときは、まず送信のログを確認してください。エラーなしで sent が出ているならペイロードは受理されており、問題はマーチャントのワークフロー側です。user_errors が出ているなら、ペイロードがデプロイ済みのトリガーのスキーマと一致していません(ほぼ必ず落とし穴 #6 です)。

現実的な最初の目標

  1. 最も重要なイベントについてトリガーを一つ、ワークフローが実際に必要とする項目だけで作ります(項目が少ないほど「必須」の罠も減ります)。キーは単語頭大文字です。
  2. 既存の状態遷移のコードから、権利のあるすべてのストアに対して送信します。購読による絞り込みはしません。
  3. 単一の runtime_url にPOSTするアクションを一つ、ハンドルで振り分け、HMACを検証し、action_run_id について冪等にし、設定画面ではなく validation_url を使って作ります。
  4. 拡張機能とバックエンドを同時にデプロイし、開発ストアで実際のワークフローを使って確認します。
  5. テンプレートは最後に追加します。Flowチームの審査が入るため、トリガーとアクションの提供を遅らせないようにしてください。

トリガーとアクションはそれぞれ小さなものです。連携の難しさは、ここまでに挙げたプラットフォームの規則に尽きます。記法と「全項目必須」の契約を初日に正しく押さえておけば、残りはすでに送信しているイベントを配線するだけです。

参考資料

関連記事

QuotWayがこれをあなたのストアでどう扱うかをご覧ください。

サイトの利用状況を把握するため、分析用Cookieを設定したいと考えています。必須ではありません。拒否してもサイトの動作は変わらず、選択はいつでも変更できます。変更はこちらから: プライバシーページ.