本文へ移動
Siteon
目次 フォーム

Docs

フォーム

data-sitekit-form を付けるだけのフォーム。表示・通知・自動返信・コンバージョンの計測(GA4・Google 広告・Meta)・テスト送信・流入元の記録。近日中に、テンプレート・公開ページ・表示の条件・重複防止・定員とキャンセル待ち・集計と CSV。

Siteon は、AI で作ったサイトのためのインフラです。AI や手で作った静的サイト(HTML)に、ホスティング・お客さん(お店の人)が直せる CMS・お問い合わせフォーム・アクセス解析・AI エージェントからの操作(CLI・MCP)を足し、そのまま納品できる状態にします。

最小の形

項目だけ書きます。送信処理・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>

名前を合わせる

HTMLschema.json
data-sitekit-form="contact" の contactforms[].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 ではなく、**管理画面の「設定 → 通知 → 各フォーム」**で、サイトのオーナーが変えます。

通知設定するもの
お店へのメールお知らせ先のメールアドレス(複数可)
SlackIncoming Webhook の URL(Slack でアプリを作り、投稿先チャンネルを選んで発行する)
Webhook(有料プラン)送り先の https の URL。管理画面の「設定 → 詳細 → Webhook」で登録し、Zapier・Make・CRM などへ署名付きの JSON を送ります(Webhook)
Google スプレッドシートへの記録 近日各自の Google アカウントでつなぎます。まれに同じ回答が 2 行入ることがあり、先頭列の ID で見分けられます
  • 通知ごとにオン・オフを切り替えられます
  • Slack の Webhook URL は、持っている人が誰でもそのチャンネルに投稿できる鍵です。管理画面は伏せ字で表示し、API も返しません。URL を失くしたら Slack で発行し直します
  • 設定を変えられるのはオーナーだけです(スタッフは見るだけ)

お礼メール(自動返信)と差し込み変数

お客様へのお礼メールは、次のときに送られます。

  1. 管理画面の「設定 → 通知 → 各フォーム」で、そのフォームの「お礼メール」がオン
  2. そのフォームに 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 の設定

  1. トリガー — 種類は「カスタム イベント」、イベント名は sitekit_form_submit(管理画面で変えたならその名前)。フォームで分けたいときは「一部のカスタム イベント」で、下の変数 Form Key が contact に等しい、のように条件を付けます
  2. 変数 — 種類は「データレイヤーの変数」。form(名前は Form Key など)と、重複を除くなら sitekit_submission_id(名前は Submission ID など)を作ります
  3. タグ — 送りたいタグの配信トリガーに 1 を指定します(下の例)

例: GA4 のキーイベント

項目値
タグの種類Google アナリティクス: GA4 イベント
測定 IDG-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 / neqselect・radio値が value と同じ / 違う
inselect・radio・checkbox_group値のどれかが value(配列)に入っている
not_emptyselect・radio・checkbox_group何か選ばれている
checkedcheckbox(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。期間外は 409 not_open / closed
  • 同時に送られても定員は超えません
  • 応答は {"ok": true, "state": "confirmed"} か {"ok": true, "state": "waitlisted", "ahead": 2}(前に待っている人数)。script は、キャンセル待ちと確認待ちのときは送信後のページへ移らず、成功の表示に「キャンセル待ちです(前に 2 人)」「確認メールを送りました」と出します
  • 自動返信の設定に関係なく、受付票のメール(状態・受付番号・取消のリンクつき)が届きます。取消のリンクから、cancel_until まで本人が取り消せます
  • 確定の席が取り消されると、キャンセル待ちの先頭が同じ処理で確定に繰り上がり、その人にメールが届きます
  • 店は管理画面「お問い合わせ」で、各申込の状態(確定・キャンセル待ち(N 番目)・確認待ちなど)を見て、「申込を取り消す」で取り消せます(CLI siteon submissions cancel、MCP cancel_submission も同じ)
  • 満席になると、アカウントのオーナーに一度だけお知らせが届きます。公開ページは page.show_remaining で残席を出せます(選択肢ごとの定員では出ません)

集計と CSV

  • 選択肢(select・radio・checkbox_group)・評価(rating、既定 1〜5)・NPS(nps、0〜10)を、受け付けるたびに数えます。管理画面「フォーム → 集計・CSV」、CLI siteon forms stats <form>、MCP get_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 を出しません

受付の上限

フォームの受付件数はプランごとに月の上限があります(予約もフォームの受付に含めて数えます)。上限に達したときの動きは プランと上限 にあります。

このページは Markdown でも読めます。AI エージェントには llms.txt(目次)か llms-full.txt(全文)を渡してください。