# Formulir HTML

> Kirim formulir HTML apa pun ke Ikiform tanpa JavaScript: redirect, field reserved, array, dan honeypot.

Formulir HTML biasa sudah merupakan seluruh integrasinya. Tanpa skrip, tanpa iframe, dan tanpa embed: atur `action` ke endpoint formulir Anda dan pertahankan markup serta CSS Anda sendiri. Ini juga berfungsi di pembuat situs yang mengizinkan Anda menempel HTML kustom; lihat [Pembuat situs](https://ikiform.com/id/docs/guides/site-builders.md).

## Formulir dasar

`contact.html`:

```html
<form action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
  <label>
    Name
    <input name="name" autocomplete="name" required />
  </label>
  <label>
    Email
    <input name="email" type="email" autocomplete="email" required />
  </label>
  <label>
    Message
    <textarea name="message" rows="5"></textarea>
  </label>
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
  <button>Send</button>
</form>
```

- `method` harus `post`. Browser mengirim `application/x-www-form-urlencoded`, atau `multipart/form-data` jika Anda mengatur `enctype`. Keduanya berfungsi.
- Setiap kontrol yang punya `name` menjadi field. Kontrol tanpa nama dan kontrol yang dinonaktifkan tidak dikirim oleh browser.
- Nilai disimpan sebagai teks UTF-8, dinormalisasi ke NFC, dengan akhir baris CRLF diubah menjadi LF dan karakter kontrol dihapus.
- Validasi browser seperti `required` dan `type="email"` membantu pengguna, tetapi bukan pemeriksaan keamanan. Definisikan [field](https://ikiform.com/id/docs/fields.md) agar Ikiform menegakkan aturan di server.

## Setelah dikirim

Kiriman browser yang berhasil dijawab dengan redirect `303`. Ikiform memilih tujuannya dengan urutan berikut:

1. Pengaturan **Redirect after submit** formulir di dashboard. URL `http:` atau `https:` apa pun bisa dipakai di sini, karena hanya anggota workspace yang bisa mengaturnya.
2. Field `_redirect` (atau `_next`) di kiriman, jika lolos aturan di bawah.
3. Jika tidak, halaman terima kasih hosted milik Ikiform di `/f/<formId>/thanks`. Jika formulir punya domain yang diizinkan dan kiriman berasal dari halaman `https:` di salah satunya, halaman itu menautkan kembali ke sana.

### Aturan untuk _redirect

Nilai `_redirect` berasal dari browser pengunjung, jadi Ikiform hanya mengikutinya jika berupa URL `https:` tanpa nama pengguna, kata sandi, atau port kustom, di salah satu domain yang diizinkan formulir atau subdomainnya. Formulir tanpa domain yang diizinkan mengabaikan `_redirect` sepenuhnya. Dengan domain yang diizinkan diatur ke `example.com`:

| _redirect | Diikuti | Alasan |
| --- | --- | --- |
| `https://example.com/thanks` | Ya | Domain yang diizinkan |
| `https://app.example.com/done` | Ya | Subdomain dari domain yang diizinkan |
| `http://example.com/thanks` | Tidak | Bukan `https:` |
| `https://example.com:8443/thanks` | Tidak | Port kustom |
| `https://user@example.com/` | Tidak | Berisi nama pengguna |
| `https://example.com.evil.dev/` | Tidak | Bukan domain itu atau subdomainnya |

`_redirect` yang tidak diikuti akan jatuh ke halaman terima kasih hosted; tidak pernah menyebabkan error. Request JSON mengabaikan redirect dan mendapat `201` dengan ID kiriman.

### Saat kiriman ditolak

Jika kiriman ditolak, misalnya karena field wajib tidak ada, browser melihat halaman error hosted dengan status HTTP dari error tersebut. Halaman itu mencantumkan masalah berdasarkan label field dan menautkan kembali ke formulir. Lihat [Error](https://ikiform.com/id/docs/errors.md) untuk setiap kode.

## Nama field dan array

Nama yang berulang dan nama yang diakhiri `[]` menjadi array. Gunakan `[]` untuk grup checkbox dan `<select multiple>` agar satu pilihan pun tetap disimpan sebagai array.

`fields.html`:

```html
<fieldset>
  <legend>Topics</legend>
  <label><input type="checkbox" name="topics[]" value="billing" /> Billing</label>
  <label><input type="checkbox" name="topics[]" value="api" /> API</label>
</fieldset>

<select name="plan">
  <option value="starter">Starter</option>
  <option value="team">Team</option>
</select>

<label><input type="checkbox" name="newsletter" /> Send me product news</label>
```

Mencentang kedua topik dan kotak newsletter akan menyimpan:

`Field yang disimpan`:

```json
{
  "topics": ["billing", "api"],
  "plan": "team",
  "newsletter": "on"
}
```

- Nama dengan kurung siku seperti `address[city]` disimpan apa adanya; tidak diubah menjadi objek bertingkat. Kirim JSON jika Anda ingin nilai bertingkat.
- Checkbox yang tidak dicentang sama sekali tidak dikirim. Jika [field](https://ikiform.com/id/docs/fields.md#types) `checkbox` didefinisikan, kotak yang tidak ada disimpan sebagai `false` dan yang dicentang sebagai `true`.
- Sebuah kiriman bisa punya hingga 100 field, nama hingga 128 karakter, dan nilai hingga 16 KB. Lihat [Batas](https://ikiform.com/id/docs/limits.md).

> Unggah file belum tersedia. Input file yang kosong diabaikan; kiriman yang berisi file sungguhan ditolak dengan `422 files_not_supported`.

## Field reserved

Nama yang diawali `_` adalah instruksi untuk Ikiform. Nama-nama ini tidak pernah disimpan sebagai jawaban.

| Field | Efek |
| --- | --- |
| `_gotcha` | Honeypot. Biarkan kosong dan tersembunyi; nilai yang terisi menambah 5 ke skor spam, yang cukup untuk menyimpan kiriman sebagai spam. Namanya bisa diubah per formulir. |
| `_redirect`, `_next` | Ke mana browser diarahkan setelah kiriman berhasil. Lihat [Aturan untuk _redirect](#redirect-rules). |
| `_subject` | Subjek untuk email notifikasi. Spasi berlebih dirapatkan dan dipotong menjadi 200 karakter. |
| `_replyto` | Alamat reply-to untuk email notifikasi. Diabaikan kecuali berupa satu alamat email yang valid. |
| `_idempotency` | Idempotency key, sama dengan header `Idempotency-Key`. Lihat [Idempotensi](https://ikiform.com/id/docs/frameworks.md#idempotency). |
| `_ikiform_ts` | Waktu render bertanda tangan, diatur oleh SDK dan komponen shadcn/ui. Kiriman yang dikirim kurang dari 2 detik setelah render dinilai sebagai spam. |
| `cf-turnstile-response` | Token Turnstile, ditambahkan oleh widget Turnstile atau SDK. |
| `_ikiform_via` | Diatur oleh halaman pemeriksaan manusia milik Ikiform. Jangan mengirimnya sendiri. |

Field lain yang diawali `_` juga dibuang, jadi Anda bisa memakai nama seperti `_source` untuk penanda tersembunyi Anda sendiri tanpa menyimpannya.

`demo.html`:

```html
<form action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
  <input name="email" type="email" required />
  <input type="hidden" name="_subject" value="New demo request" />
  <input type="hidden" name="_redirect" value="https://example.com/thanks" />
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
  <button>Request a demo</button>
</form>
```

## Honeypot

Nama honeypot default adalah `_gotcha`. Anda bisa mengubahnya per formulir di **Honeypot field** pada pengaturan formulir; nama apa pun yang Anda pilih diperlakukan sebagai reserved dan tidak pernah disimpan. Atribut `hidden` biasa sudah cukup untuk sebagian besar bot. Jika spam masih lolos, sembunyikan input di luar layar, karena sebagian bot melewati input yang ditandai `hidden`:

`honeypot.html`:

```html
<div aria-hidden="true" style="position:absolute;left:-10000px;width:1px;height:1px;overflow:hidden">
  <label>
    Leave this empty
    <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" />
  </label>
</div>
```

Pertahankan `tabindex="-1"` dan `autocomplete="off"` agar pengguna keyboard dan pengelola kata sandi tidak pernah mengisinya.

## Menambahkan pemeriksaan manusia

Formulir dengan pemeriksaan spam yang diatur ke **Ikiform** tidak perlu perubahan apa pun di HTML Anda. Saat browser mengirim tanpa token, Ikiform menjawab dengan halaman singkat "Memeriksa bahwa Anda manusia" di ikiform.com. Halaman itu menjalankan Cloudflare Turnstile, lalu mengirim ulang field aslinya dengan token. Pengunjung Anda berakhir di halaman terima kasih yang sama seperti sebelumnya. Lihat [Perlindungan spam](https://ikiform.com/id/docs/spam.md#human-check) untuk alur lengkapnya.

### Dengan key Turnstile Anda sendiri

Jika formulir memakai **Your own Turnstile keys**, render widget-nya sendiri. Widget itu menambahkan field `cf-turnstile-response` ke kiriman. Kiriman tanpa token yang valid ditolak dengan `403 captcha_failed`.

`turnstile.html`:

```html
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>

<form action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
  <input name="email" type="email" required />
  <div class="cf-turnstile" data-sitekey="YOUR_SITE_KEY"></div>
  <button>Send</button>
</form>
```
