> ## Documentation Index
> Fetch the complete documentation index at: https://docs.raykoi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Creating a Form

> Build a schema in the dashboard, then integrate it anywhere.

<Steps>
  <Step title="Create the form">
    From your workspace, click **New form** and give it a name. This creates the form immediately — there's no separate "draft" vs "published" state to manage. It's live the moment it exists, accepting submissions by default.

    <Frame>
      <img src="https://mintcdn.com/raykoi/OUWOqoE2CmHs9Dj9/images/dashboard/create-form-modal.png?fit=max&auto=format&n=OUWOqoE2CmHs9Dj9&q=85&s=38df4591df2941cfaadc00dc678c6e99" alt="Create a new form dialog in the Raykoi dashboard" width="564" height="520" data-path="images/dashboard/create-form-modal.png" />
    </Frame>
  </Step>

  <Step title="Add fields">
    Build the schema field-by-field — text, email, phone, number, select/multiselect, radio, checkbox, date/time, file, signature, rating/scale, and more. Each field has:

    * **Required** — enforced both client-side (instant feedback) and server-side (the actual gate — see [Submissions](/essentials/submissions))
    * **Validation rules** — length/range bounds, or a pattern for text fields
    * **A submission key** — defaults to the field's ID, overridable if you need your integration's field names to differ from the field's internal ID

    Whatever you build shows up as JSON if you fetch the form's schema — this is what [Get a Form's Schema](/api-reference/get-form-schema) returns for a form with an email and a message field:

    ```json theme={null}
    {
      "schema": {
        "fields": [
          { "id": "email", "type": "email", "label": "Email", "required": true },
          { "id": "message", "type": "textarea", "label": "Message", "required": false, "maxLength": 1000 }
        ]
      }
    }
    ```
  </Step>

  <Step title="Configure spam protection">
    New forms start with honeypot and rate limiting on (always on, no configuration needed) but no CAPTCHA. Turn on CAPTCHA from the form's **Spam Protection** settings if you expect real public traffic — see [Cloudflare Turnstile](/essentials/captcha/turnstile), [hCaptcha](/essentials/captcha/hcaptcha), or [reCAPTCHA v3](/essentials/captcha/recaptcha) to set one up.
  </Step>

  <Step title="Restrict origins (optional)">
    By default a form accepts submissions from any origin — useful while you're testing locally or embedding on multiple sites. Add specific domains under **Allowed Origins** once you know exactly where the form will live, to reject submissions from anywhere else.

    <Frame>
      <img src="https://mintcdn.com/raykoi/OUWOqoE2CmHs9Dj9/images/dashboard/access-restrictions-spam-protection.png?fit=max&auto=format&n=OUWOqoE2CmHs9Dj9&q=85&s=f5298eec09d05715c4cc45cf0b956e24" alt="Access & restrictions and Spam protection panels in the form settings" width="1564" height="300" data-path="images/dashboard/access-restrictions-spam-protection.png" />
    </Frame>

    Pausing submissions and restricting origins are Starter+ features — a Free-plan workspace sees these locked, as shown above.
  </Step>

  <Step title="Get your integration code">
    Every form has a **Get Started** tab with copy-paste code for every supported framework, pre-filled with that form's actual public ID — the same content as [Integrations](/integrations/overview), scoped to this one form.
  </Step>
</Steps>

## Closing a form

Turn off **Accepting submissions** in the form's settings (Starter plan or above) to stop new submissions without deleting the form or its existing data. A closed form returns `423` to anyone who tries to submit — see [Submissions](/essentials/submissions#a-closed-form).

## Deleting a form

Deletes the form and everything attached to it — submissions, uploaded files, analytics, webhook configuration — in one action. There's no soft-delete or recovery window, so this is worth double-checking before you confirm it.
