ID formulir

Referensi SDK

Paket @ikiform/sdk: submit, challenge, getSchema, validate, createClient, dan verifyWebhook.

@ikiform/sdk adalah paket TypeScript kecil untuk mengirim formulir, membaca data dari REST API, dan memverifikasi webhook. Paket ini tanpa dependency dan hanya memakai fetch, FormData, dan Web Crypto, jadi berjalan di browser, Node 20+, Bun, Deno, dan Cloudflare Workers. Paket dirilis sebagai ESM dengan deklarasi tipe.

Terminal
npm install @ikiform/sdk
ImporDariBerjalan di
submit@ikiform/sdkBrowser atau server
challenge, ChallengeError@ikiform/sdkBrowser
getSchema, validate, validateField@ikiform/sdkBrowser atau server
createClient, IkiformError@ikiform/sdkHanya server
verifyWebhook, WebhookVerificationError@ikiform/sdk/webhooksServer

submit

submit(formId, data, options?) mengirim ke /f/:formId dan resolve ke sebuah hasil. Fungsi ini tidak pernah melempar error: error HTTP mengembalikan envelope error, kegagalan jaringan mengembalikan kode network_error, dan pembatalan mengembalikan aborted.

SubmitResult
type SubmitResult =
  | { ok: true; id: string }
  | {
      ok: false;
      error: { code: string; message: string; fields?: Record<string, string[]> };
      status: number | null;
      requestId: string | null;
    };
dataDikirim sebagai
FormData yang hanya berisi teksapplication/x-www-form-urlencoded
FormData dengan Filemultipart/form-data, yang ditolak Ikiform sampai fitur unggah tersedia
URLSearchParamsapplication/x-www-form-urlencoded
Objek biasaapplication/json
  • Field $ACTION_ milik React dibuang dari FormData.
  • Nilai kembaliannya juga memenuhi tipe form action React, jadi action={(data) => submit(id, data)} berfungsi.
  • submit mengirim Accept: application/json. Fungsi ini tidak mengirim header Authorization; gunakan fetch untuk mengirim dengan kunci API.
PilihanDeskripsi
baseUrlDefault-nya https://ikiform.com.
signalAbortSignal untuk membatalkan request.
idempotencyKeyDikirim sebagai header Idempotency-Key.
turnstileTokenDikirim sebagai cf-turnstile-response dalam format body apa pun yang dipakai.
fetchImplementasi fetch kustom.
submit.ts
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

Untuk formulir yang pemeriksaan spamnya Ikiform. challenge(formId, options?) memasang frame ikiform.com selebar 300 piksel yang tetap bertinggi nol kecuali Turnstile butuh klik, resolve dengan token, lalu menghapus frame. Token hanya sekali pakai dan berlaku 300 detik, jadi panggil sekali per kiriman.

PilihanDeskripsi
containerTempat memasang frame. Default-nya document.body.
mode"frame" (default) atau "popup", untuk browser yang memblokir frame. Popup harus dibuka dari sebuah klik.
timeoutMsReject setelah sekian milidetik. Tanpa timeout secara default.
signalSebuah AbortSignal.
baseUrlDefault-nya https://ikiform.com.

Fungsi ini me-reject dengan ChallengeError, yang code-nya challenge_failed, challenge_timeout, atau aborted.

challenge.ts
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?) mengambil skema publik formulir: field yang dipublikasikan, pengaturan tampilan, nama honeypot, dan mode challenge. Seperti submit, fungsi ini tidak pernah melempar error, dan resolve ke { ok: true, schema } atau hasil error. Opsinya adalah baseUrl, signal, dan fetch. Responsnya dijelaskan di halaman shadcn/ui.

validate

validate(fields, data) memeriksa FormData atau objek biasa terhadap definisi field dan mengembalikan Record<string, string>, satu pesan per field yang tidak valid, atau objek kosong. Fungsi ini memakai aturan dan pesan yang sama seperti server untuk ke-12 tipe field. validateField(field, value) memeriksa satu nilai dan mengembalikan pesan atau undefined.

validate.ts
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? }) mengembalikan client untuk REST API. Fungsi ini melempar TypeError jika key bukan secret key, jadi simpan di server.

MetodeRequest
forms.list({ limit, cursor })GET /forms
forms.get(formId)GET /forms/:formId
forms.iterate({ limit })Setiap formulir, mengikuti cursor
submissions.list({ formId, status, limit, cursor })GET /submissions
submissions.get(id)GET /submissions/:id
submissions.iterate({ formId, status, limit })Setiap kiriman, mengikuti cursor
submissions.markSpam(id)POST /submissions/:id/spam
submissions.markNotSpam(id)POST /submissions/:id/not-spam
submissions.delete(id)DELETE /submissions/:id

Kegagalan melempar IkiformError dengan code, status, requestId, message, plus fields jika ada dan retryAfter dalam detik pada 429. Kegagalan jaringan melempar dengan code: "network_error" dan status: 0.

client.ts
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? }) memverifikasi tanda tangan webhook dan mengembalikan event yang sudah diurai. Berikan string body mentah. Toleransi default-nya 300 detik ke dua arah.

webhook.ts
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,
});

Error verifikasi

reasonArti
invalid_secretSecret-nya bukan whsec_ ditambah base64
missing_headerswebhook-id, webhook-timestamp, atau webhook-signature tidak ada
invalid_timestampwebhook-timestamp bukan detik Unix bulat
timestamp_too_oldDitandatangani lebih dari toleranceSeconds yang lalu, misalnya request yang dikirim ulang (replay)
timestamp_too_newDitandatangani di masa depan; periksa jam server Anda
no_matching_signatureTidak ada tanda tangan v1 yang cocok: secret salah, atau body sudah diubah
invalid_payloadTanda tangan cocok tetapi body-nya bukan JSON
Tipe event diekspor sebagai WebhookEvent, SubmissionCreatedEvent, dan EndpointTestEvent. Lihat Webhook untuk payload lengkapnya.