SDK リファレンス
@ikiform/sdk パッケージ: submit、challenge、getSchema、validate、createClient、verifyWebhook。
@ikiform/sdk は、フォームの送信、REST API からのデータの読み取り、Webhook の検証を行う小さな TypeScript パッケージです。依存関係はなく、fetch、FormData、Web Crypto だけを使うので、ブラウザー、Node 20 以降、Bun、Deno、Cloudflare Workers で動きます。型宣言付きの ESM として提供しています。
npm install @ikiform/sdk| インポート | インポート元 | 実行環境 |
|---|---|---|
submit | @ikiform/sdk | ブラウザーまたはサーバー |
challenge, ChallengeError | @ikiform/sdk | ブラウザー |
getSchema, validate, validateField | @ikiform/sdk | ブラウザーまたはサーバー |
createClient, IkiformError | @ikiform/sdk | サーバーのみ |
verifyWebhook, WebhookVerificationError | @ikiform/sdk/webhooks | サーバー |
submit
submit(formId, data, options?) は /f/:formId に送信し、結果に解決されます。例外は投げません。HTTP エラーではエラーの情報を、ネットワークエラーではコード network_error を、中断では aborted を返します。
type SubmitResult =
| { ok: true; id: string }
| {
ok: false;
error: { code: string; message: string; fields?: Record<string, string[]> };
status: number | null;
requestId: string | null;
};| data | 送信形式 |
|---|---|
テキストだけの FormData | application/x-www-form-urlencoded |
File を含む FormData | multipart/form-data。アップロードが実装されるまで Ikiform は拒否します |
URLSearchParams | application/x-www-form-urlencoded |
| 単純なオブジェクト | application/json |
- React の
$ACTION_フィールドはFormDataから除外されます。 - 戻り値は React のフォームアクションの型も満たすので、
action={(data) => submit(id, data)}と書けます。 submitはAccept: application/jsonを送ります。Authorizationヘッダーは送らないので、API キーを付けて送信するにはfetchを使ってください。
| オプション | 説明 |
|---|---|
baseUrl | デフォルトは https://ikiform.com。 |
signal | リクエストをキャンセルするための AbortSignal。 |
idempotencyKey | Idempotency-Key ヘッダーとして送られます。 |
turnstileToken | どのボディの形式でも、cf-turnstile-response として送られます。 |
fetch | 独自の fetch の実装。 |
import { submit } from "@ikiform/sdk";
const result = await submit("frm_xxxxxxxxxxxxxxxx", { email: "ada@example.com" }, {
idempotencyKey: crypto.randomUUID(),
signal: AbortSignal.timeout(10_000),
});
if (!result.ok) console.error(result.error.code, result.error.fields);challenge
スパムチェックが Ikiform のフォーム向けです。challenge(formId, options?) は幅 300 ピクセルの ikiform.com のフレームをマウントします。フレームは Turnstile がクリックを必要としない限り高さ 0 のままで、トークンで resolve されるとフレームは削除されます。トークンは 1 回限りで有効期間は 300 秒なので、送信ごとに 1 回呼び出してください。
| オプション | 説明 |
|---|---|
container | フレームをマウントする場所。デフォルトは document.body。 |
mode | "frame"(デフォルト)または "popup"。フレームをブロックするブラウザー向けです。ポップアップはクリックから開く必要があります。 |
timeoutMs | このミリ秒数が経過すると reject します。デフォルトではタイムアウトなし。 |
signal | AbortSignal。 |
baseUrl | デフォルトは https://ikiform.com。 |
code が challenge_failed、challenge_timeout、aborted のいずれかの ChallengeError で reject されます。
import { challenge, ChallengeError, submit } from "@ikiform/sdk";
async function send(form) {
try {
const turnstileToken = await challenge("frm_xxxxxxxxxxxxxxxx", { timeoutMs: 60_000 });
return submit("frm_xxxxxxxxxxxxxxxx", new FormData(form), { turnstileToken });
} catch (error) {
if (!(error instanceof ChallengeError) || error.code !== "challenge_failed") throw error;
const turnstileToken = await challenge("frm_xxxxxxxxxxxxxxxx", { mode: "popup" });
return submit("frm_xxxxxxxxxxxxxxxx", new FormData(form), { turnstileToken });
}
}getSchema
getSchema(formId, options?) はフォームの公開スキーマを取得します。公開済みのフィールド、表示設定、ハニーポットの名前、チャレンジのモードです。submit と同じく例外は投げず、{ ok: true, schema } かエラーの結果に解決されます。オプションは baseUrl、signal、fetch です。レスポンスは shadcn/ui のページで説明しています。
validate
validate(fields, data) は FormData または単純なオブジェクトをフィールド定義と照合し、無効なフィールドごとに 1 つのメッセージを持つ Record<string, string>、または空のオブジェクトを返します。12 種類すべてのフィールドタイプで、サーバーと同じルールとメッセージを使います。validateField(field, value) は 1 つの値を確認し、メッセージか undefined を返します。
import { getSchema, submit, validate } from "@ikiform/sdk";
const result = await getSchema("frm_xxxxxxxxxxxxxxxx");
if (result.ok) {
const errors = validate(result.schema.fields, new FormData(form));
if (Object.keys(errors).length === 0) {
await submit("frm_xxxxxxxxxxxxxxxx", new FormData(form));
}
}createClient
createClient({ apiKey, baseUrl?, fetch? }) は REST API のクライアントを返します。キーがシークレットキーでない場合は TypeError を投げるので、サーバー上だけで使ってください。
| メソッド | リクエスト |
|---|---|
forms.list({ limit, cursor }) | GET /forms |
forms.get(formId) | GET /forms/:formId |
forms.iterate({ limit }) | カーソルをたどって、すべてのフォーム |
submissions.list({ formId, status, limit, cursor }) | GET /submissions |
submissions.get(id) | GET /submissions/:id |
submissions.iterate({ formId, status, limit }) | カーソルをたどって、すべての送信データ |
submissions.markSpam(id) | POST /submissions/:id/spam |
submissions.markNotSpam(id) | POST /submissions/:id/not-spam |
submissions.delete(id) | DELETE /submissions/:id |
失敗すると、code、status、requestId、message に加え、ある場合は fields、429 の場合は秒数を示す retryAfter を持つ IkiformError を投げます。ネットワークエラーでは code: "network_error"、status: 0 で投げます。
import { createClient, IkiformError } from "@ikiform/sdk";
const ikiform = createClient({ apiKey: process.env.IKIFORM_SECRET_KEY! });
const forms = await ikiform.forms.list({ limit: 10 });
const form = await ikiform.forms.get("frm_xxxxxxxxxxxxxxxx");
for await (const submission of ikiform.submissions.iterate({ formId: form.id, status: "received" })) {
console.log(submission.id, submission.fields);
}
try {
await ikiform.submissions.delete("sub_01k7v2c9t3e8m5q4x6z0a1b2c3");
} catch (error) {
if (error instanceof IkiformError && error.status === 429) {
console.log(`Retry in ${error.retryAfter} seconds`);
}
}verifyWebhook
verifyWebhook({ payload, headers, secret, toleranceSeconds? }) は Webhook の署名を検証し、解析済みのイベントを返します。生のボディの文字列を渡してください。デフォルトの許容範囲は前後 300 秒です。
import { verifyWebhook, type WebhookEvent } from "@ikiform/sdk/webhooks";
const event = await verifyWebhook<WebhookEvent>({
payload: rawBody,
headers: request.headers,
secret: process.env.IKIFORM_WEBHOOK_SECRET!,
toleranceSeconds: 300,
});検証エラー
| reason | 意味 |
|---|---|
invalid_secret | シークレットが whsec_ と base64 の形式ではない |
missing_headers | webhook-id、webhook-timestamp、webhook-signature のいずれかがない |
invalid_timestamp | webhook-timestamp が整数の Unix 秒ではない |
timestamp_too_old | toleranceSeconds より前に署名された。リプレイされたリクエストなど |
timestamp_too_new | 未来の時刻で署名された。サーバーの時計を確認してください |
no_matching_signature | 一致する v1 署名がない。シークレットが間違っているか、ボディが変更された |
invalid_payload | 署名は一致したが、ボディが JSON ではない |
WebhookEvent、SubmissionCreatedEvent、EndpointTestEvent としてエクスポートされています。ペイロードの全体は Webhook を参照してください。