JavaScript と fetch
送信エンドポイントの詳細: ボディの形式、ヘッダー、CORS、レスポンス、冪等性。
HTTP リクエストを送れるコードなら何でも Ikiform に送信できます。JSON、FormData、urlencoded のデータを https://ikiform.com/f/<formId> に POST し、JSON の応答を読み取ります。SDK はこれを 1 つの関数にまとめていますが、使うかどうかは任意です。特定のスタックでの手順はプラットフォーム別ガイドを参照してください。
fetch を使う
const response = await fetch("https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx", {
method: "POST",
headers: { "Content-Type": "application/json", Accept: "application/json" },
body: JSON.stringify({
name: "Ada Lovelace",
email: "ada@example.com",
message: "Hello",
}),
});
const result = await response.json();
if (!result.ok) console.error(result.error.code, result.error.fields);試してみる
curl https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx \
-H "Content-Type: application/json" \
-d '{"name":"Ada Lovelace","email":"ada@example.com","message":"Hello from the Ikiform docs."}'自分のフォーム ID を入力すると、実際のリクエストとして送信できます。
エンドポイント
| メソッド | パス | 内容 |
|---|---|---|
POST | /f/<formId> | 送信データを作成 |
OPTIONS | /f/<formId> | CORS のプリフライト。常に 204 |
GET | /f/<formId>/schema | フォームの公開フィールド定義。shadcn/ui を参照 |
GET | /f/<formId>/thanks | ホストされたサンクスページ |
GET | /f/<formId>/challenge | SDK が使う、Ikiform の人間確認フレーム |
ブラウザーのタブで /f/<formId> そのものを開くと GET になり、404 not_found が返ります。これは想定どおりの動作で、エンドポイントは POST しか受け付けません。
ボディの形式
| Content-Type | 形式 | メモ |
|---|---|---|
application/json と +json 系のタイプ | 1 つの JSON オブジェクト | 値には文字列、数値、真偽値、配列、オブジェクトを使えます。ネストはトップレベルのオブジェクトを含めて最大 3 階層です |
application/x-www-form-urlencoded | 名前と値のペア | 繰り返される名前と name[] は配列になります |
multipart/form-data | 名前と値のペア | urlencoded と同じ。実際のファイルは拒否されます |
| それ以外 | 415 unsupported_media_type |
ボディは UTF-8 である必要があります。utf-8 以外の charset は 415 unsupported_charset になり、UTF-8 として無効なバイトも同じコードになります。JSON の配列や不正な形式のボディは 400 invalid_body になります。
ヘッダー
リクエストヘッダー
| ヘッダー | 必須 | 効果 |
|---|---|---|
Content-Type | はい | ボディのパーサーを決めます。上記を参照 |
Accept | いいえ | application/json で JSON の応答を強制します。JSON かリダイレクトかを参照 |
Idempotency-Key | いいえ | リトライを安全にします。最大 255 文字。冪等性を参照 |
Authorization | いいえ | Bearer ik_sk_... または Bearer ik_pub_...。サーバーから送信するを参照 |
Origin, Referer | ブラウザーが送信 | フォームの許可ドメインと照合されます |
レスポンスヘッダー
| ヘッダー | タイミング | 値 |
|---|---|---|
X-Request-Id | すべてのレスポンス | req_...。エラーのボディでは request_id として繰り返されます |
Retry-After | 429 | 次の送信が許可されるまでの秒数 |
Location | ブラウザーでの成功時の 303 | リダイレクト先、またはホストされたサンクスページ |
Access-Control-Allow-Origin | リクエストに Origin がある場合 | リクエスト自身のオリジン |
Access-Control-Expose-Headers | CORS の場合 | X-Request-Id, Retry-After。ブラウザーのコードから両方を読み取れます |
Cache-Control | 成功時 | no-store |
CORS
送信エンドポイントは、どのサイトからのクロスオリジンの送信も受け付けます。プリフライトは常に成功し、リクエストのオリジンをそのまま返します。Cookie や認証情報は使わないため、フォームを守るのは CORS ではなく Allowed domains(許可ドメイン)です。許可ドメインを設定すると、他のサイトからの送信はフォームの設定に応じてスパムとして保存されるか、拒否されます。許可ドメインを参照してください。
OPTIONS /f/frm_xxxxxxxxxxxxxxxx HTTP/1.1
Origin: https://example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type, Accept, Idempotency-Key, Authorization
Access-Control-Max-Age: 86400
Vary: Origin/api/v1 以下の REST API は異なります。CORS ヘッダーを送らず、ブラウザーのプリフライトには 405 で応答するため、シークレットキーが Web ページから使われることはありません。レスポンス
成功すると、新しい送信 ID 付きの 201 を返します。エラーはどこでも同じ形式を使い、バリデーションに失敗した場合はフィールド ID ごとにフィールドエラーを返します。すべてのコードはエラーにあります。
{ "ok": true, "id": "sub_01k7v2c9t3e8m5q4x6z0a1b2c3" }201 を返します。スパムは別に保存され、Webhook や受信トレイに届くことはありません。冪等性
リトライを安全にするには、Idempotency-Key ヘッダーか _idempotency フィールドを送ります。キーは最大 255 文字で、フォーム単位で 24 時間有効です。両方送った場合はヘッダーが優先されます。
| リトライの内容 | 結果 |
|---|---|
| 同じキーで同じフィールド | 最初の応答を再送します。2 件目の送信データは作成されません。 |
| 同じキーで異なるフィールド | 422 idempotency_mismatch |
| 最初のリクエストの処理中に同じキー | 409 idempotency_in_progress |
バリデーションエラーなど、送信データが保存される前に失敗した送信はキーを保持しないので、データを直して同じキーでリトライできます。安全なリトライのループ:
const key = crypto.randomUUID();
async function send(attempt = 1) {
const response = await fetch("https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx", {
method: "POST",
headers: { "Content-Type": "application/json", "Idempotency-Key": key },
body: JSON.stringify({ email: "ada@example.com" }),
}).catch(() => null);
if (response && response.status < 500 && response.status !== 429) return response.json();
if (attempt === 3) throw new Error("Ikiform is unreachable");
const wait = Number(response?.headers.get("Retry-After")) || attempt * 2;
await new Promise((resolve) => setTimeout(resolve, wait * 1000));
return send(attempt + 1);
}プラットフォーム別ガイド
フレームワーク
- Next.jsクライアントコンポーネント、または Server Action。
- React送信中、エラー、成功の状態。
- Vue単一ファイルコンポーネント。
- Nuxtハイドレーション前は通常の送信、後は
$fetchで送信。 - SvelteSvelte 5 のコンポーネントと Server Action。
- SvelteKitプログレッシブエンハンスメント付きのプリレンダリングページ。
- AstroJavaScript なしのフォーム。必要に応じて拡張。
- Gatsby静的なフォーム、または Gatsby Function。
- 素の JavaScriptインラインエラー付きの
fetch。
静的サイトジェネレーター
- Hugoパーシャルと、コンテンツファイル用のショートコード。
- Jekyllインクルードと、GitHub Pages の設定。
- EleventyNunjucks、Liquid、Markdown 用のショートコード。