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.
npm install @ikiform/sdk| Impor | Dari | Berjalan di |
|---|---|---|
submit | @ikiform/sdk | Browser atau server |
challenge, ChallengeError | @ikiform/sdk | Browser |
getSchema, validate, validateField | @ikiform/sdk | Browser atau server |
createClient, IkiformError | @ikiform/sdk | Hanya server |
verifyWebhook, WebhookVerificationError | @ikiform/sdk/webhooks | Server |
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.
type SubmitResult =
| { ok: true; id: string }
| {
ok: false;
error: { code: string; message: string; fields?: Record<string, string[]> };
status: number | null;
requestId: string | null;
};| data | Dikirim sebagai |
|---|---|
FormData yang hanya berisi teks | application/x-www-form-urlencoded |
FormData dengan File | multipart/form-data, yang ditolak Ikiform sampai fitur unggah tersedia |
URLSearchParams | application/x-www-form-urlencoded |
| Objek biasa | application/json |
- Field
$ACTION_milik React dibuang dariFormData. - Nilai kembaliannya juga memenuhi tipe form action React, jadi
action={(data) => submit(id, data)}berfungsi. submitmengirimAccept: application/json. Fungsi ini tidak mengirim headerAuthorization; gunakanfetchuntuk mengirim dengan kunci API.
| Pilihan | Deskripsi |
|---|---|
baseUrl | Default-nya https://ikiform.com. |
signal | AbortSignal untuk membatalkan request. |
idempotencyKey | Dikirim sebagai header Idempotency-Key. |
turnstileToken | Dikirim sebagai cf-turnstile-response dalam format body apa pun yang dipakai. |
fetch | Implementasi fetch kustom. |
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.
| Pilihan | Deskripsi |
|---|---|
container | Tempat memasang frame. Default-nya document.body. |
mode | "frame" (default) atau "popup", untuk browser yang memblokir frame. Popup harus dibuka dari sebuah klik. |
timeoutMs | Reject setelah sekian milidetik. Tanpa timeout secara default. |
signal | Sebuah AbortSignal. |
baseUrl | Default-nya https://ikiform.com. |
Fungsi ini me-reject dengan ChallengeError, yang code-nya challenge_failed, challenge_timeout, atau aborted.
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.
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.
| Metode | Request |
|---|---|
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.
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.
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
| reason | Arti |
|---|---|
invalid_secret | Secret-nya bukan whsec_ ditambah base64 |
missing_headers | webhook-id, webhook-timestamp, atau webhook-signature tidak ada |
invalid_timestamp | webhook-timestamp bukan detik Unix bulat |
timestamp_too_old | Ditandatangani lebih dari toleranceSeconds yang lalu, misalnya request yang dikirim ulang (replay) |
timestamp_too_new | Ditandatangani di masa depan; periksa jam server Anda |
no_matching_signature | Tidak ada tanda tangan v1 yang cocok: secret salah, atau body sudah diubah |
invalid_payload | Tanda tangan cocok tetapi body-nya bukan JSON |
WebhookEvent, SubmissionCreatedEvent, dan EndpointTestEvent. Lihat Webhook untuk payload lengkapnya.