---
title: "Creating forms"
description: "Three ways to start a form - describe it to the AI form builder, start from a template, or start empty - in the dashboard, the REST API and MCP."
---

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

# Creating forms

There are three ways to start a new form. The new form screen in the dashboard offers all three, and the [REST API](https://docs.getquickforms.com/api/) and [MCP server](https://docs.getquickforms.com/mcp/) can do the same.

| Way to start | Dashboard | REST API | MCP |
| --- | --- | --- | --- |
| Describe the form in plain English | Describe it | `POST /api/v1/forms/generate` | `generate-form` |
| Start from a ready-made template | Start from a template | `GET /api/v1/form-templates`, then `POST /api/v1/forms` with `template` | `list-form-templates`, then `create-form` with `template` |
| Start with your own fields | Start empty | `POST /api/v1/forms` with `name` and `schema` | `create-form` with `name` and `schema` |

Whichever way you start, the form is saved through the same action and checked against the same [schema rules](https://docs.getquickforms.com/schema/), so a form made one way can be edited any other way.

A new form is published as soon as it is created, unless you pass `draft: true`. Then it starts unpublished and takes sandbox submissions only until you publish it. See [create an unpublished form](https://docs.getquickforms.com/drafts-and-publishing/#create-an-unpublished-form).

## Start from a template

Templates are ready-made drafts: a name, a description for the [spam filter](https://docs.getquickforms.com/spam-filter/), a schema and settings. Some also turn on the autoresponder or add a [smart rule](https://docs.getquickforms.com/spam-filter/#smart-rules).

| Key | Name | Category | Asks for | Also sets |
| --- | --- | --- | --- | --- |
| `contact` | Simple contact form | General | Name (required), Email (required), Message (required) | Autoresponder |
| `service-business` | Service business enquiry | Sales | Name (required), Email (required), Phone, What do you need? (required), Preferred date, Tell us a bit more (required) | Autoresponder; smart rule "Must be an enquiry about our services, not a sales pitch to us" |
| `quote-request` | Quote request | Sales | Name (required), Work email (required), Company (required), Budget, Timeline, Project details (required) | Smart rule "Must describe a real project they want quoted" |
| `newsletter` | Newsletter signup | Marketing | Email (required), First name, I agree to receive emails (must be ticked) | Notification email off |
| `waitlist` | Product waitlist | Marketing | Work email (required), Company, Team size, What would you use it for? | Autoresponder |
| `content-download` | Content download | Marketing | First name (required), Last name (required), Work email (required), Company (required), Job title, Send me occasional product news | Autoresponder; smart rule "Must use a company email address, not a personal one" |
| `feedback` | Customer feedback | Product | How would you rate us? (required), What went well?, What could be better?, Email (if you would like a reply) | Nothing extra |
| `bug-report` | Bug report | Product | Email (required), How bad is it? (required), Steps to reproduce (required), Page where it happened, Screenshot | Nothing extra |
| `event-rsvp` | Event RSVP | Events | Name (required), Email (required), Can you make it? (required), Extra guests, Dietary requirements | Autoresponder |
| `job-application` | Job application | Hiring | Full name (required), Email (required), Role you are applying for (required), LinkedIn or portfolio, CV (required), Why you? | Autoresponder |

### Create a form from a template

Pass `"template": "<key>"` when you create a form. The template's fields and settings are used, and anything else you pass overrides them, so `name` and `schema` become optional. An unknown key is a 422 on `template`.

The example below uses illustrative values.

### REST API

```bash
curl -X POST https://YOUR-QUICKFORMS-HOST/api/v1/forms \
  -H "Authorization: Bearer $QUICKFORMS_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"template": "event-rsvp", "name": "Summer party RSVP", "mode": "sandbox"}'
```

### MCP

```json
{
  "tool": "create-form",
  "arguments": { "template": "event-rsvp", "name": "Summer party RSVP", "mode": "sandbox" }
}
```

The response is the created form, the same as any [`POST /api/v1/forms`](https://docs.getquickforms.com/api/#post-apiv1forms).

> **Note**
>
> Anything you pass overrides the template. Settings are merged key by key, but lists replace the template's list instead of merging into it: pass `schema.fields` to use your own fields, and `settings.notify.emails`, `settings.allowed_origins`, `settings.spam.rules` or `settings.honeypot_fields` to replace those lists.

### List the templates

`GET /api/v1/form-templates` returns every template with its `key`, `name`, `summary`, `category`, `fields` (the field labels) and the full `draft`. The MCP tool `list-form-templates` returns the same list without the `draft`. See [the API reference](https://docs.getquickforms.com/api/#get-apiv1form-templates) for an example response.

## Describe it to the AI form builder

Describe the form in plain English, for example "a quote request form for a garden landscaping business", and the AI form builder returns a draft. The draft has:

- A name and a schema of fields.
- A description, which the [spam filter](https://docs.getquickforms.com/spam-filter/#the-form-description) reads as context.
- Up to 3 [smart rules](https://docs.getquickforms.com/spam-filter/#smart-rules) for the spam filter.
- A success message.
- An autoresponder, turned on or off, with a subject and body.

Nothing is saved until you say so. In the dashboard the draft opens in the editor. Through the API and MCP you get the draft back, or pass `save: true` to create the form in one step.

> **Note**
>
> The draft is written by an AI model. Review the fields, the smart rules and the autoresponder text before you save it or put it live, and send it a [sandbox](https://docs.getquickforms.com/sandbox/) test submission.

### What happens to the model's answer

The builder asks an OpenAI model for a form in a fixed structure, then cleans the answer before you see it:

- Fields with a type quickforms doesn't support are dropped.
- Field names are made safe (lowercase letters, numbers and underscores) and unique within the form.
- Choice fields (`select`, `radio`, `checkboxes`) with no options are dropped.
- Only the field keys that matter for each type are kept, and `min` and `max` are dropped from date fields.
- The result is validated like any other [schema](https://docs.getquickforms.com/schema/).

### Generate a draft

The example prompt and draft below are illustrative. The model's answer varies from request to request.

### REST API

```bash
curl -X POST https://YOUR-QUICKFORMS-HOST/api/v1/forms/generate \
  -H "Authorization: Bearer $QUICKFORMS_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "A quote request form for a garden landscaping business", "save": false}'
```

### MCP

```json
{
  "tool": "generate-form",
  "arguments": { "prompt": "A quote request form for a garden landscaping business", "save": false }
}
```

### 200 response

```json
{
  "data": {
    "draft": {
      "name": "Landscaping quote",
      "description": "Homeowners asking for a quote for garden landscaping work.",
      "schema": {
        "fields": [
          { "name": "name", "type": "text", "label": "Name", "required": true },
          { "name": "email", "type": "email", "label": "Email", "required": true },
          { "name": "postcode", "type": "text", "label": "Postcode", "required": true },
          { "name": "work", "type": "checkboxes", "label": "What work do you need?", "required": true, "options": ["Design", "Planting", "Paving", "Fencing"] },
          { "name": "details", "type": "textarea", "label": "Tell us about your garden", "required": false }
        ]
      },
      "settings": {
        "success_message": "Thanks, we will be in touch about your quote.",
        "autoresponder": { "enabled": true, "subject": "Thanks {{name}}, we got your quote request", "body": "..." },
        "spam": { "rules": ["Must be a request for garden landscaping work"] }
      }
    }
  }
}
```

The draft is in the same shape `POST /api/v1/forms` and `create-form` accept, so you can edit it and send it straight back to create the form. With `save: true` the response is the created form with status `201`, the same as [`POST /api/v1/forms`](https://docs.getquickforms.com/api/#post-apiv1forms).

### Errors and limits

| Status | When |
| --- | --- |
| `422` on `prompt` | The prompt is shorter than 5 or longer than 2000 characters. |
| `422` on `prompt` | The builder is not configured (no `OPENAI_API_KEY`, or `QUICKFORMS_FORM_BUILDER` is off). |
| `422` on `prompt` | The model could not create a form. Try again. |
| `422` on `schema.*` | The cleaned draft still fails schema validation. |
| `429` | The user has used their generated forms for the hour. |

Generating is rate limited to 30 forms an hour per user by default (`QUICKFORMS_FORM_BUILDER_PER_HOUR`). The MCP tool returns an error saying to try again later instead of a `429`.

### Configuration

The builder uses `laravel/ai` with the OpenAI provider.

| Variable | Default | What it does |
| --- | --- | --- |
| `OPENAI_API_KEY` | none | OpenAI API key. The builder is unavailable without it. |
| `QUICKFORMS_FORM_BUILDER` | `true` | Turn the AI form builder on or off. |
| `QUICKFORMS_FORM_BUILDER_MODEL` | `gpt-6-luna` | OpenAI model used to design forms. |
| `QUICKFORMS_FORM_BUILDER_PER_HOUR` | `30` | Generated forms allowed per user per hour. |

## Start empty

Create a form with a `name` and your own `schema`. See the [schema reference](https://docs.getquickforms.com/schema/) for every field type and option, and [`POST /api/v1/forms`](https://docs.getquickforms.com/api/#post-apiv1forms) for a full example.

Source: https://docs.getquickforms.com/creating-forms/index.mdx
