There are three ways to start a new form. The new form screen in the dashboard offers all three, and the REST API and MCP server 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, 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.
Start from a template
Templates are ready-made drafts: a name, a description for the spam filter, a schema and settings. Some also turn on the autoresponder or add a smart rule.
| 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.
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"}'{
"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.
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 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 reads as context.
- Up to 3 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.
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
minandmaxare dropped from date fields. - The result is validated like any other schema.
Generate a draft
The example prompt and draft below are illustrative. The model’s answer varies from request to request.
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}'{
"tool": "generate-form",
"arguments": { "prompt": "A quote request form for a garden landscaping business", "save": false }
}{
"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.
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 for every field type and option, and POST /api/v1/forms for a full example.