回答とWebhook
受信者から返ってくるすべて — 投票への回答、フォームの送信、評価、スピン抽選の結果 — はプロジェクトごとに収集されます。これは2つの方法であなたに返されます: ダッシュボードの受信者ごとの回答ビューと、届いた瞬間に各インタラクションを自分のエンドポイントへプッシュする任意のWebhookです。
ダッシュボードでの受信者ごとの回答
ダッシュボード → 対象のメール → 回答を開きます。そのメールが収集したすべてのインタラクションは受信者ごとにグループ化され、プロジェクトのデータソースと結合されます。各グループには、誰が答えたか(メールアドレスとその行の他のフィールド)と、何をしたか(どのブロック、どのアクション、送信された値、いつ)が並んで表示されます。
帰属付けは、ライブビューのリンクを保護しているのと同じ署名付きトークンによって機能します。データソースからメールが個人化されている場合、各受信者のリンクにはその行に紐付いたトークンが含まれ、有効なトークンとともに届いたインタラクションはその行に帰属します。トークンなしで届いたインタラクション(例:個人化なしで送信されたメールからの投票)もカウントされますが、これらは別の匿名グループに一覧表示されます。
生のイベント一覧はページ分割されています(新しい順): 最新のページを読み込んだ後、さらに読み込むアクションで過去のイベントを取得できます。以下のすべて — 集計、ヒストグラム、ファネル、トレンド — はこのページ分割の影響を受けません。事前計算されており、常にプロジェクトの全履歴を対象としています。
注文、フルフィルメント、返金
商品ブロックを持つプロジェクトには、回答ページに注文テーブルが追加されます — チェックアウト1件につき1行、購入者、金額、状態、注文日時を表示します。
- 決済が確定した瞬間、購入者には自動的にメールが送信されます: デジタル商品の場合はその配信コンテンツ、物理的な注文の場合はフルフィルメント作業中であることを示す確認メールです。
- 物理的な注文については、発送が完了したら発送済みにするをクリックしてください — MailInApp自体は配送を扱いませんが、スイッチを切り替えた瞬間に購入者へ発送済みの通知メールを送信します。
- 支払い済みの注文で返金をクリックすると、接続したStripeアカウントを通じて決済が取り消され、購入者に返金確認メールが送信されます。最後の1点が売れた瞬間に決済が確定した場合も同じメールが自動的に送信されます — MailInAppは黙って過剰販売するのではなく、購入者に返金します。
- 購入者に連絡は、その注文のコンテキストがあらかじめ入力されたサポートチケットを開きます — 購入者に直接何かを尋ねる最も手早い方法です。
目的別のヘッドライン指標
プロジェクトに目的(アンケート、プロモーション、ニュースレター、イベント)が設定されている場合、回答ページはその目的が最も重視する1つの数値を先頭に表示します: アンケートなら回答率、プロモーションならエンゲージした受信者数、ニュースレターとイベントならトラッキングされた開封数です。トランザクション用プロジェクトと目的が設定されていないプロジェクトは、これを飛ばして下記のファネルと集計に直接進みます。目的はページタイトルの横のドロップダウンからいつでも変更・解除できます。表示される数値だけに影響し、記録される内容には一切影響しません。
CSAT、CES、NPSの集計
すべての評価ブロックには、受信者一覧の上に集計が表示されます: 回答数、平均値、そしてヒストグラム(平均の背後にある分布 — 4.1という平均が、すべて4点なのか5点と1点の混在なのかを明らかにします)。NPS形式のブロックは、単純な平均の代わりに推奨者/中立者/批判者の件数と−100〜100のスコアを表示します。回答の分布の内訳は、投票ブロックについても選択肢ごとに同様に表示されます。
エンゲージメントファネル(送信済み → 開封済み(概算) → 回答済み)が集計の上に表示されます。ブロックごとの回答トレンドチャート(日単位または週単位)は、時間とともに平均がどう変化したかを示します — 繰り返し実施するアンケートの価値は、1回のスナップショットではなく傾向線にあります。ブロックに少なくとも2つ分のバケットのデータがない場合は表示されません。
アンケート質問の集計
すべてのフォームブロックには、評価ブロックや投票ブロックと同じように質問ごとの集計が付きます。選択式、チェックボックス、リニアスケール、星評価の質問には選択肢ごとの件数を示す棒グラフが表示され、自由記述の質問(短文、長文、メールアドレス、数値、電話番号、日付)には回答数といくつかの回答例が表示されます。複数ページのアンケートも単一ページのものと同じように集計されます — すべてのページが完了した時点で初めて1件の送信としてカウントされるため、途中で放棄されたアンケートが部分的な回答として表示されることはありません。
これらの投票、評価、アンケート質問の結果はそこで終わりではありません — どれも、チャートブロックを「キャンペーンの回答」に紐付けることで、今後のメールに直接プロットできます。手作業での再入力は不要です。チャートを実際のデータに紐付けるを参照してください。
宿題と成績表
完了または提出が1件以上ある課題ブロックには、成績表テーブルの上に統計カード(完了数、遅延完了数、提出数)が表示されます。このテーブルは、生徒1人につき1行、課題1つにつき1列で、緑色で「完了」または送信された回答を表示し、ブロックの期限より後に届いた場合は赤色で表示します。まだ回答のない課題はテーブルを煩雑にしません。他のすべての集計と同様に事前計算済みで、プロジェクトの全履歴を対象とし、CSVエクスポートの両方にブロックごとの課題列が含まれます。
クリックヒートマップ
集計の下に、クリックヒートマップが、単純なリンクを持つすべてのブロック(ボタン、リンク付き画像、ソーシャルアイコン)をクリック数順にランキング表示し、相対的な量に応じてバーの長さと濃淡が調整されます。何がカウントされ、何がカウントされないかについてはクリックの記録方法を参照してください。
スクロール深度ファネル
スクロール深度ファネルは、ライブビューの訪問者のうち何割がページの25/50/75/100%の各マイルストーンに到達したかを示すため、早い段階で離脱しているのか最後まで読まれているのかを把握できます。これはホスティングされたライブビューへの訪問のみを反映し、送信されたメール自体は反映されません — その理由についてはスクロール深度を参照してください。
A/Bテストと自動勝者選定
複数のバリアントで送信する(送信を参照)と、回答ページにバリアント比較が追加されます: 各バリアントの開封率、クリック率、回答率が並べて表示され、テストで使用した指標に基づいて現在のリーダーにフラグが付きます。すべての受信者はメールアドレスから確定的にバリアントに割り当てられるため、再送信や再試行によって誰がどのバリアントを見たかが入れ替わることはありません。結果はプロジェクトについて累積されており、これまでに実行したすべてのA/B送信を対象とします。直近の1回だけではありません。
バリアントは件名の文言だけに限定されません。送信者情報、あるいはプロジェクトのコンテンツ全体を切り替えることもできます。コンテンツが異なるバリアント自身の投票/クイズ/RSVPの回答や開封は、この比較ではなくそのプロジェクト自身の回答ページで追跡されます。インタラクションの検証は、送信をレンダリングした正確なプロジェクトに結び付けられているためです。比較パネルは、誤解を招くゼロを表示する代わりに、そちらへのリンクを表示します。
自動的に勝者を選ぶは、手動のA/Bテストを自動実行するテストに変えます。テストの割合(例:リストの20%をバリアント間で分割して送信)、判定までの待機時間、そして判定に使う指標(開封、クリック、メール内での回答、収益)を選びます。メール内での回答(投票/クイズ/RSVPの完了)は、Appleのメールプライバシー保護の影響を開封トラッキングのようには受けないため、推奨される既定の指標です。
待機時間が経過すると、MailInAppは受信者ごとの割合が最も良いバリアントを選びます。これは決して総数そのものではないため、テスト中に単に多くの人に送信されただけのバリアントが、量だけで勝者に見えることはありません。勝者は、最初のテストから除外されていた全員に送信されます。結果が同点だった場合や、サンプルが小さすぎた場合、MailInAppはバリアント1にフォールバックし、誤った勝者を宣言することのないよう、その旨を明確にラベル表示します。回答ページのステータスバナーには、テストが現在どの状態にあるか(判定中、判定済み、同点、サンプル不足)が正確に表示されます。
送信時最適化
Proプラン以上で利用できます。送信対象の全員に一斉に送信する代わりに、各受信者にとって最適な時間に送信するは、各連絡先自身の開封時刻の履歴(UTCで、実際に最もあなたのメールを開封している時間帯)を確認します。その時間帯までメッセージを保留します。信頼できる信号を得るのに十分な開封履歴がまだない相手は、そのまま通常の即時送信になります。
これは手動送信、繰り返しスケジュール、ジャーニー自身の送信ステップのいずれにも同じように適用されます。信号のない相手には、スケジュールは依然として設定した時刻に全員へ送信し、最適化前と同じ動作になります。同じ送信内で自動勝者選定テストと組み合わせることは現時点ではできません。保留された受信者は、勝者が判定される時点でカウントされないためです。
フィールドで分割する
内訳の基準を使うと、データソースの任意のフィールドで同じ集計をバケット分割できます — エージェントごとの平均CSAT、プランごとのNPSなどです。送信後に行が削除または縮小された受信者は**(不明)にまとめられ、自由記述のフィールドが20を超える異なる値を生成した場合は、表示が煩雑にならないよう、最小のバケットが末尾のその他**グループにまとめられます。
CSVエクスポート
回答ページのCSVをダウンロードは、受信者1人につき1行という横長のファイルをエクスポートします: すべてのデータソースフィールド、最初の開封時刻、そしてインタラクティブブロックごとに1列(その質問文が見出しになります)です。フォローアップの回答は<質問> — follow-upという独自の列を持ちます。もう1つのraw eventsエクスポートは、イベントごとに1行(受信者、ブロック、アクション、値、タイムスタンプ)という、ロング形式を求める分析担当者向けの形式です。
アンケートのライフサイクル
プロジェクトには締切日と/または回答数の上限(maxResponses)を設定できます。いずれかに達すると、新しいインタラクションイベントは拒否されます — ライブビューはインタラクティブブロックの代わりに終了案内を表示します(静的なコンテンツは引き続き表示されます) — そして回答ページには、アンケートが現在締め切られているかどうかが表示されます。何も遡って除外されることはありません: 既に記録された回答はそのままデータに残ります。
アプリ内の低評価アラート
WebhookのlowScoreフラグ(下記)に加えて、プロジェクトには数件までのメールアドレスを登録し、Zapier/Makeのステップを介さず直接通知することができます。評価の回答が設定済みのしきい値を下回るたびに、MailInAppは自分のSMTP設定を通じて、それらのアドレスに質問内容、スコア、(わかる場合)受信者の識別情報、そして回答ページへの直接リンクをメールで送信します。SMTPリレーが設定されていない場合はアラートは黙ってスキップされますが、Webhookは引き続き発火します。アラートはプロジェクトごと・1時間あたりに上限があるため、低評価のバーストによってリレーが溢れることはありません。
集計の再構築
集計、ファネル、トレンドは、イベントの到着とともに更新される事前計算済みのプロジェクト単位の集計から提供されます — そのため、プロジェクトの履歴量にかかわらず高速な状態を保ちます。プロジェクトの合計値が予想より低い、またはゼロに見える場合、その集計がまだ存在する前にプロジェクトが回答を収集していた可能性があります。そのプロジェクトの回答ページで集計を再構築を一度クリックすると、イベント履歴全体を集計に再生できます。新しいプロジェクトではこれは不要です。
Webhook
データを自分のシステム(CRM、スプレッドシート、オートメーションツール)に取り込みたい場合は、同じ回答ページでWebhookを設定します: HTTPS URLを入力して有効化をクリックします。
2つのことが起こります:
- 署名用シークレット(
whsec_…)が表示されます — すぐにコピーしてください。表示されるのはこの一度だけです。MailInAppの他のすべての認証情報と同様に、サーバー側に保存され、その後はどこでもマスクされます。 - それ以降、すべてのインタラクションは記録された直後にJSONとして自分のURLへPOSTされます。
シークレットはローテーション(新しいものが発行され、一度だけ表示されます)、またはWebhookを削除することもいつでもできます。
ペイロード
{
"type": "interaction.received",
"projectId": "abc123",
"event": {
"campaignId": "abc123",
"blockId": "poll-1",
"blockType": "poll",
"action": "vote",
"value": { "option": "Blue" },
"recipient": "row:3",
"projectId": "abc123",
"receivedAt": 1752480000000
},
"recipient": {
"key": "row:3",
"row": { "email": "[email protected]", "first_name": "Ada" }
},
"lowScore": false
}
event.valueは収集されたデータそのものです — 選択された投票の選択肢、フォームのフィールド値、星の数などです。recipientは匿名のインタラクションではnullになります。帰属付けられたものについては、keyはデータソース内での受信者の行インデックスです("row:3"= 4行目)。recipient.row(受信者の完全なデータ行)はhostedタイプのデータソースについてのみ含まれます。APIタイプのソースについては、すべてのインタラクションであなたのエンドポイントを呼び出すことはしません — 自分側で行インデックスを使って結合してください。lowScoreは、評価ブロックに対するrateアクションのイベントで、その値がそのブロックに設定されたアラートのしきい値以下だった場合にtrueになります — これは、Zapier/Makeのオートメーション(または自分のアプリ内アラート)がサポートマネージャーを呼び出すためにフィルタリングする信号です。評価以外のイベント、またはブロックにしきい値が設定されていない場合は、このフィールドは完全に省略されます。
署名を検証する
すべての配信は署名されているため、自分のエンドポイントで、それが本当にMailInAppから送られたものであることを確認できます。2つのヘッダーが送信されます:
| ヘッダー | 内容 |
| --- | --- |
| X-MailInApp-Timestamp | 配信が署名された時刻、エポックミリ秒 |
| X-MailInApp-Signature | v1=の後に続く、16進のHMAC-SHA256(secret, timestamp + "." + rawBody) |
期待される署名を(JSONパース前の)生のリクエストボディから計算し、定数時間比較で比較してください。古いタイムスタンプを拒否することで、再送された配信をブロックできます:
import { createHmac, timingSafeEqual } from "node:crypto";
function isValidDelivery(headers, rawBody, secret) {
const timestamp = headers["x-mailinapp-timestamp"];
const given = Buffer.from(headers["x-mailinapp-signature"] ?? "");
const expected = Buffer.from(
"v1=" +
createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`)
.digest("hex"),
);
if (given.length !== expected.length || !timingSafeEqual(given, expected)) {
return false;
}
// Reject deliveries signed more than 5 minutes ago (replay protection).
return Math.abs(Date.now() - Number(timestamp)) < 5 * 60 * 1000;
}
配信の仕組み
- 初回の配信は即時で、その後は自動的に再試行されます。 配信は5秒でタイムアウトします。
2xxに満たないもの(タイムアウト、接続失敗、エラーステータス)はすべて失敗として扱われます。イベントは常に先にMailInApp側に保存されるため、配信を1件見逃しても何も失われません — Webhookはリアルタイムの信号として、回答ビューは信頼できる情報源として扱ってください。 - バックオフを伴う自動再試行。 失敗した配信は、Webhookの現在のURLとシークレットに対して、およそ1分、5分、30分、2時間、6時間という間隔を空けて再試行されます。そのため、ローテーションされたシークレットや更新されたURLも自動的に反映されます。すべての再試行が失敗しても、それ以上自動で再試行されなくなるだけで、その配信データが破棄されることはありません。
- 手動での再送信。 現在も失敗している配信(再試行中、または再試行済み)は、回答ページの失敗した配信に表示され、直前の失敗の理由と再送信ボタンが付きます — 自分側の問題を修正した直後など、次の予定された再試行を待たずに使うと便利です。
- 受信者を待たせない。 配信は受信者のインタラクションが確認された後に行われるため、遅い、または壊れたエンドポイントが受信者の投票や送信を遅延・失敗させることはありません。
- すぐに応答する。
2xxをすぐに返し、重い処理は非同期で行ってください。
信頼に関する補足: インタラクションエンドポイントは必然的に公開されています(受信箱側で認証はできません)。そのため匿名のイベントは設計上認証なしです。帰属付けられたイベントは署名付きの受信者トークンによって保護されています。配信の署名を検証し、
event.valueはユーザー入力として扱ってください。