最小の形
項目だけ書きます。送信処理・hidden 項目・エラー表示の JavaScript は要りません(script タグ は入れておきます)。
<form data-sitekit-form="contact">
<label>お名前 <input name="name" required autocomplete="name"></label>
<label>メール <input type="email" name="email" required autocomplete="email"></label>
<label>お問い合わせ内容 <textarea name="message"></textarea></label>
<!-- ボット対策。人には見えない場所に置く(必須) -->
<input type="text" name="_hp" tabindex="-1" autocomplete="off"
style="position:absolute;left:-9999px" aria-hidden="true">
<button type="submit">送信する</button>
</form>
<div data-sitekit-success role="status" aria-live="polite" hidden>お問い合わせありがとうございました。</div>
<div data-sitekit-error role="status" aria-live="polite" hidden></div>名前を合わせる
| HTML | schema.json |
|---|---|
data-sitekit-form="contact" の contact | forms[].key |
入力の name="email" | その form の fields[].key |
- schema.json に無いフォームのキーは
not_foundになります - schema.json に無い
nameはエラーにならず、保存もされません requiredの項目は、空だとinvalid_requestになります(項目ごとの文字数などの制約は schema.json の型)
ハニーポット(_hp)は必ず入れる
name="_hp" の入力は、人には見えないボット対策の欄です。ここが埋まっている送信は保存されません。ただしボットに気づかせないため、画面上は成功と同じに見えます。
tabindex="-1"とautocomplete="off"を付け、画面の外に置きます(ブラウザの自動入力で埋まらないように)- schema.json には書きません
成功・エラーの表示
[data-sitekit-success] と [data-sitekit-error] は、フォームの中か、フォームと同じ親要素の直下に置きます。
- 成功するとフォームが
hiddenになります。成功の文言はフォームの外に置きます(中に置くと一緒に隠れる) role="status" aria-live="polite"を付けます。付けないと、送信後に出た文言がスクリーンリーダーに読み上げられません- エラーのときは
[data-sitekit-error]に次の文言が入ります
| 状況 | 表示される文言 |
|---|---|
入力の誤り(invalid_request) | 入力内容をご確認ください |
短時間に送りすぎ(rate_limited) | しばらく経ってからお試しください |
Turnstile の確認に失敗(turnstile_failed) | 確認に失敗しました。もう一度お試しください |
| 通信できない | 送信できませんでした。通信環境をご確認ください |
| そのほか | 送信できませんでした。時間をおいてお試しください |
重複回答の防止・定員つきのフォームでは、次も出ます。
状況(error) | 表示される文言 |
|---|---|
回答済み(already_submitted) | 回答済みです |
満席(full) 近日 | 満席です |
受付期間外(not_open / closed) 近日 | 受付期間外です |
入力の誤りのときは、該当する項目に aria-invalid="true" が付きます。見た目はサイト側の CSS で当てます。
[aria-invalid="true"] { border-color: #c00; }送信後に別ページへ移動する
<form data-sitekit-form="contact" data-success-url="/thanks/">同じオリジンのページだけです。別のオリジンへ移動するときは、明示して許可します。
<form data-sitekit-form="reserve"
data-success-url="https://booking.example.com/done"
data-success-external="1">スキームやポートが違えば別オリジンです。javascript: と data: は data-success-external を付けても移動しません。
二重送信の防止
連打や、入力欄での Enter による再送信は自動で止めます。送信中は送信ボタンが disabled になり、<form> に aria-busy="true" が付きます。form="フォームのid" でフォームの外に置いた送信ボタンにも効きます。
ファイルの添付
<input type="file" name="photo" accept="image/jpeg,image/png,image/webp,application/pdf"> を置くと、写真や PDF を受け取れます。管理画面でフォームに「ファイル」の項目を足すと、埋め込みコードにもこの欄が出ます。
- 受け取れるのは画像(JPEG・PNG・WebP)と PDF。1 ファイル 10MB、1 回の送信で 3 ファイル・合計 20MB まで。サイトの写真容量に含まれます
- 1 つの項目に付けられるファイルは 1 つです(
multipleや同じ名前の欄を並べると、送信を止めて知らせます) - 届いたファイルは、管理画面の受付の詳細からダウンロードします(リンクは 15 分で切れます)。通知メールにはファイルを付けず、「添付ファイルあり」と管理画面へのリンクを送ります。Webhook・CSV・スプレッドシートにもファイルは出ません
- 送信に失敗したとき(大きすぎる・形式が違うなど)は、送信を止めて理由を表示します。ファイルが黙って捨てられることはありません
- 予約ページのフォームには、いまはファイルの項目を付けられません
- 埋め込みスクリプトを自分のサイトにコピーして置いている場合は、新しい版に差し替えてください。古い版のままだと、ファイルの項目があるフォームは送信できません
複数の値を持つ項目
チェックボックスの群や <select multiple> など、同じ name を複数使うと最後の値だけが送られます。複数の答えが要る質問は、キーを分けます。
<!-- NG: topic が1つしか届かない -->
<input type="checkbox" name="topic" value="a">
<input type="checkbox" name="topic" value="b">
<!-- OK -->
<input type="checkbox" name="topic_a" value="1">
<input type="checkbox" name="topic_b" value="1">チェックボックスの群(checkbox_group)
同じ name のチェックボックスを、チェックされた値が全部届く形にできます。schema では項目の type を checkbox_group にし、options に選択肢を書きます。
<label><input type="checkbox" name="topic" value="price"> 料金</label>
<label><input type="checkbox" name="topic" value="hours"> 営業時間</label>checkbox_group 以外の項目に同じ name を複数置くと、最後の値だけが保存されます。
Turnstile
Cloudflare Turnstile のウィジェットが作る cf-turnstile-response の入力があれば、自動で一緒に送ります。サイト側で特別な記述は要りません。
通知
お問い合わせは保存された時点で確定します。通知はそのあとに送られ、どの通知が失敗してもお問い合わせは消えません。各通知の成否は管理画面のお問い合わせの詳細に出て、失敗したものは再送できます。
通知先は schema.json ではなく、**管理画面の「設定 → 通知 → 各フォーム」**で、サイトのオーナーが変えます。
| 通知 | 設定するもの |
|---|---|
| お店へのメール | お知らせ先のメールアドレス(複数可) |
| Slack | Incoming Webhook の URL(Slack でアプリを作り、投稿先チャンネルを選んで発行する) |
| Webhook(有料プラン) | 送り先の https の URL。管理画面の「設定 → 詳細 → Webhook」で登録し、Zapier・Make・CRM などへ署名付きの JSON を送ります(Webhook) |
| Google スプレッドシートへの記録 近日 | 各自の Google アカウントでつなぎます。まれに同じ回答が 2 行入ることがあり、先頭列の ID で見分けられます |
- 通知ごとにオン・オフを切り替えられます
- Slack の Webhook URL は、持っている人が誰でもそのチャンネルに投稿できる鍵です。管理画面は伏せ字で表示し、API も返しません。URL を失くしたら Slack で発行し直します
- 設定を変えられるのはオーナーだけです(スタッフは見るだけ)
お礼メール(自動返信)と差し込み変数
お客様へのお礼メールは、次のときに送られます。
- 管理画面の「設定 → 通知 → 各フォーム」で、そのフォームの「お礼メール」がオン
- そのフォームに
type: "email"の項目があり、値が入っている
件名と本文は「設定 → 通知 → 各フォーム」で書きます(プレビューあり)。差し込める変数:
| 変数 | 中身 |
|---|---|
{{name}} {{email}} など | フォームの項目の値(項目の key そのまま) |
{{site_name}} | サイト名 |
{{form_label}} | フォームの名前(forms[].label) |
{{submitted_at}} | 送信日時 |
{{name}} 様
{{site_name}} へのお問い合わせありがとうございます。
内容を確認のうえ、ご連絡いたします。
受付日時: {{submitted_at}}送信を GTM で拾う
送信が成功したときだけ(受け付けて保存し終えたあとに1回)、dataLayer に次が積まれます。送信ボタンを押した時点や、通信に失敗したときには積まれません。
{
event: "sitekit_form_submit", // 完了イベント名(フォームごとに変えられる)
form: "contact", // フォームのキー(data-sitekit-form の値)
sitekit_form: "contact", // 同じ値(form と同じ)
sitekit_site: "hanaya", // サイト ID(data-site)
sitekit_submission_id: "…" // 受付 ID。コンバージョンの重複を除くのに使う
}- 入力内容(名前・メールアドレスなど)は入れません。 広告や解析のタグは受け取った値を外部に送るので、個人情報を載せないためです
- イベント名は管理画面の「設定 → 通知 → 各フォーム」の完了イベント名で変えられます。既定は
sitekit_form_submitです。名前は送信の応答で届くので、サイトの再デプロイは要りません。名前を変えたら GTM のトリガーも直します(古い名前のトリガーは黙って動かなくなります) - ハニーポットで弾いた送信も画面上は成功なので、このイベントは積まれます(このとき
sitekit_submission_idはnull)。実際に届いた件数は管理画面の一覧で数えます
GTM を使わない場合(sitekit:submit イベント)
同じ内容を detail に持つ DOM イベント sitekit:submit が document に送られます。GA4 や Meta のタグを自分で書くサイトはこちらを拾います。
<script>
document.addEventListener("sitekit:submit", (e) => {
const d = e.detail; // { event, form, sitekit_form, sitekit_site, sitekit_submission_id }
gtag("event", "generate_lead", { form_key: d.sitekit_form, transaction_id: d.sitekit_submission_id ?? undefined });
fbq("track", "Lead", { content_name: d.sitekit_form }, { eventID: d.sitekit_submission_id ?? undefined });
});
</script>GTM の設定
- トリガー — 種類は「カスタム イベント」、イベント名は
sitekit_form_submit(管理画面で変えたならその名前)。フォームで分けたいときは「一部のカスタム イベント」で、下の変数Form Keyがcontactに等しい、のように条件を付けます - 変数 — 種類は「データレイヤーの変数」。
form(名前はForm Keyなど)と、重複を除くならsitekit_submission_id(名前はSubmission IDなど)を作ります - タグ — 送りたいタグの配信トリガーに 1 を指定します(下の例)
例: GA4 のキーイベント
| 項目 | 値 |
|---|---|
| タグの種類 | Google アナリティクス: GA4 イベント |
| 測定 ID | G-XXXXXXX(GA4 の設定タグと同じ) |
| イベント名 | generate_lead(GA4 の推奨イベント名。ほかの名前でもよい) |
| イベント パラメータ | form_key = {{Form Key}} |
| 配信トリガー | 上のトリガー |
GA4 の管理画面の「イベント」で generate_lead をキーイベントとしてマークすると、コンバージョンとして数えられます。form_key を付けておくと、どのフォームからの問い合わせかを GA4 で分けられます。
例: Google 広告のコンバージョン(トランザクション ID)
| 項目 | 値 |
|---|---|
| タグの種類 | Google 広告のコンバージョン トラッキング |
| コンバージョン ID / ラベル | Google 広告で「コンバージョン アクション」(カテゴリは「リードフォームの送信」など)を作ると表示される値 |
| トランザクション ID | {{Submission ID}}(同じ送信が二重に数えられるのを防ぐ) |
| 配信トリガー | 上のトリガー |
同じコンテナに「コンバージョン リンカー」タグ(すべてのページで配信)が無ければ足します。Meta のピクセルで重複を除くときは、eventID に同じ受付 ID を渡します(上の sitekit:submit の例)。gclid は script が流入元として別に記録しているので、オフラインのコンバージョンの取り込みに使うなら、管理画面の CSV か Webhook で読めます。
確かめ方
GTM のプレビュー(Tag Assistant)でサイトを開き、フォームを実際に送ります。左のイベントの一覧に完了イベントが出て、「Data Layer」タブに form と sitekit_submission_id が入っていれば設定できています。
テスト送信
いちばん確かなのは、公開したページ(開発用ドメインでもよい)から実際に送ることです。API に直接送ることもできます。
curl -s -X POST -H 'content-type: application/json' \
-d '{"name":"テスト","email":"[email protected]","message":"テスト送信です"}' \
https://cdn.siteon.dev/v1/sites/hanaya/forms/contact
# => {"ok":true,"id":"...","gtm_event":"sitekit_form_submit"}テストでも本物のお問い合わせとして保存され、通知も届きます。 件数の上限にも数えられます。試したあとは管理画面から消してください。CLI の siteon forms test(MCP の send_test)でも送れます(CLI・MCP・API キー)。
流入元(UTM)の記録
script が覚えておいた流入元(最初に来たときと最後に来たときの utm_* / gclid / fbclid・着地ページ・参照元)が、送信のたびに自動で添えられ、お問い合わせと一緒に保存されます。サイト側の作業はありません(script タグ)。
テンプレートから作る
よくある形は、項目と設定の入ったテンプレートから作れます。管理画面の「フォーム → テンプレートから追加」、CLI の siteon forms create --template <名前>、MCP の create_form のどれでも同じものができます。
| テンプレート | 中身 |
|---|---|
contact | お問い合わせ(名前・メール・電話・内容) |
survey 近日 | アンケート。満足度(5 段階)・NPS・良かった点(複数選択)・自由記述。個人情報の項目を含まない設定(下の「集計と CSV」) |
application 近日 | 申込。希望日時ごとの定員とキャンセル待ち、メール確認つき(下の「定員」「重複回答の防止」) |
brochure | 資料請求(会社名・連絡先・欲しい資料) |
job | 求人応募(連絡先・希望職種・志望理由) |
作ったあとは、項目も設定も普通のフォームと同じように直せます。
survey は同じブラウザでの重複防止を、application は定員とメール確認を使うので、この 2 つで作ったフォームは、それらの機能が開くまで受け付けません 近日(survey は dedupe を none か phone に変えれば受け付けます)。
公開ページ(サイトが無くても使える)
フォームごとに、Siteon がホストする公開ページを出せます(管理画面「フォーム → 公開」、siteon forms publish <form>)。
- URL は
https://<共通ドメイン>/f/<ページ ID>。QR コード(SVG)と、ほかのサイトに貼る埋め込みコード(iframe)も出ます - サイトの公開(deploy)は要りません。Free プランのページには、小さな「Made with Siteon」のバッジが付きます
- 送信は Cloudflare Turnstile で守られ、JavaScript が無くても送れます
- 止めると、ページも受付も止まります(もう届いた申込の確認・取消のリンクは動きます)
- 決済に使う語(カード番号・暗証番号など)を含むフォームは、Free プランでは保存できず、有料プランでは公開の前に自動の検査に回ります(フォームでカード情報を集めないでください)
表示の条件(show_if)
「〇〇を選んだときだけ出す」項目は、schema の項目に show_if を書きます。参照できるのは同じフォームの select / radio / checkbox_group / checkbox で、連鎖は 3 段までです。
op | 参照先 | 意味 |
|---|---|---|
eq / neq | select・radio | 値が value と同じ / 違う |
in | select・radio・checkbox_group | 値のどれかが value(配列)に入っている |
not_empty | select・radio・checkbox_group | 何か選ばれている |
checked | checkbox(value なし)・checkbox_group(value 1 つ) | チェックされている |
{ "key": "detail", "label": "詳しく", "type": "textarea", "show_if": { "field": "topic", "op": "eq", "value": "other" } }条件に合わない項目は、required でも空で送れ、値を送っても保存されません(判定はサーバーが行います)。
公開ページでは自動で隠れます。自分のサイトに埋め込むときは、項目を次の枠で包むと script が隠します(隠れた項目は送られず、required でも止まりません)。
<div data-sitekit-field="detail" data-sitekit-show-if='{"field":"topic","op":"eq","value":"other"}'>
<label>詳しく <textarea name="detail" required></textarea></label>
</div>管理画面「サイトに組み込む」が出すフォームの HTML と AI 向けの指示文には、この枠が入っています。
重複回答の防止(dedupe)
同じ人の 2 回目を止める方法を、フォームごとに選べます。
dedupe | 何で同じ人とみなすか | 限界 |
|---|---|---|
none(既定) | 止めない(通信の再送による二重送信だけ止まる) | 同じ人の 2 回目は 2 件 |
email_verify 近日 | メールアドレス(Gmail の別名は同じとみなす)。確認メールのリンクを開いて確定した回答だけが有効 | 別のアドレスを持てば別人 |
browser 近日 | 同じブラウザ(公開ページは cookie、埋め込みは script が localStorage に置く値) | 別のブラウザ・シークレットウィンドウ・履歴の消去で回避できる。保存を拒むブラウザでは止まらない |
phone | 電話番号(数字だけにそろえる。+81 は 0) | 番号の持ち主は確かめない(補助にとどまる) |
email_verifyとbrowserの受付は準備中です 近日(いまはnoneとphoneが使えます)- 2 回目は 409
already_submitted(script は「回答済みです」と出す) email_verify: 送信すると確認メールが届き、24 時間以内(定員つきはconfirm_untilまで)にリンクを開いて「確定する」を押すと有効になります。確認前に同じアドレスで送ると、確認メールを送り直します(1 時間 3 通まで)。期限が切れた回答は数えず、同じアドレスでまた申し込めます- 判定に使う値は鍵つきのハッシュでだけ保存し、元の cookie の値などは持ちません
定員・キャンセル待ち(capacity) 近日
申込フォーム(kind: "application")に定員を付けられます。
| キー | 意味 |
|---|---|
limit | 定員(1〜10,000)。必須 |
waitlist | キャンセル待ちの枠(0〜10,000、既定 0) |
by_field / limits | 選択肢ごとの定員(例: 希望日時ごと)。by_field は必須の select / radio。limits で選択肢ごとに別の数にできる |
opens_at / closes_at | 受付の期間(タイムゾーンつきの ISO 8601。例 2026-11-01T10:00:00+09:00) |
confirm_until | メール確認の期限(email_verify のとき) |
cancel_until | お客様が取り消せる期限 |
event_at / remind_before_hours | 当日の日時と、何時間前にリマインドを送るか(1〜168) |
- 席があれば確定、満席ならキャンセル待ち、待ちも一杯なら 409
full。期間外は 409not_open/closed - 同時に送られても定員は超えません
- 応答は
{"ok": true, "state": "confirmed"}か{"ok": true, "state": "waitlisted", "ahead": 2}(前に待っている人数)。script は、キャンセル待ちと確認待ちのときは送信後のページへ移らず、成功の表示に「キャンセル待ちです(前に 2 人)」「確認メールを送りました」と出します - 自動返信の設定に関係なく、受付票のメール(状態・受付番号・取消のリンクつき)が届きます。取消のリンクから、
cancel_untilまで本人が取り消せます - 確定の席が取り消されると、キャンセル待ちの先頭が同じ処理で確定に繰り上がり、その人にメールが届きます
- 店は管理画面「お問い合わせ」で、各申込の状態(確定・キャンセル待ち(N 番目)・確認待ちなど)を見て、「申込を取り消す」で取り消せます(CLI
siteon submissions cancel、MCPcancel_submissionも同じ) - 満席になると、アカウントのオーナーに一度だけお知らせが届きます。公開ページは
page.show_remainingで残席を出せます(選択肢ごとの定員では出ません)
集計と CSV
- 選択肢(select・radio・checkbox_group)・評価(
rating、既定 1〜5)・NPS(nps、0〜10)を、受け付けるたびに数えます。管理画面「フォーム → 集計・CSV」、CLIsiteon forms stats <form>、MCPget_form_stats。NPS は推奨(9–10)の割合 − 批判(0–6)の割合 - CSV はサイトのオーナーだけ(管理画面、CLI
siteon forms export <form>)。Excel でそのまま開ける BOM つき UTF-8。列は id・日時・状態・担当・申込の状態・枠・版・各項目・メモ数。1 回 50,000 件まで(多いときは期間で分ける)。書き出しは履歴に残ります - 回答を消しても集計の数は戻りません(CSV は消した後の回答だけ)
no_personal_fields: trueの個人情報の項目を含まないアンケートは、メール・電話の項目を置けず、ブラウザの情報・流入元を保存せず、時刻は時間単位で持ちます。回答の本文に書かれたことは残りますemit_submission_id: falseにすると、計測(dataLayer・sitekit:submit)に回答の id を出しません
受付の上限
フォームの受付件数はプランごとに月の上限があります(予約もフォームの受付に含めて数えます)。上限に達したときの動きは プランと上限 にあります。