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

# Add a contact form

> Add a working contact form to a Cactal website: post to /_cactal/forms/contact, enhance it with fetch so visitors keep their input, test it on the draft preview, and read submissions over the API.

A form needs no configuration: post to `/_cactal/forms/{name}` and Cactal stores the submission, emails the recipients, and lists it in the **Forms** tab. See [Forms](https://cactal.ai/docs/concepts/forms.md) for the full contract.

## Prerequisites

* A website and an API key with any access role on it, see [Create an API key](https://cactal.ai/docs/create-an-api-key.md)
* Familiarity with editing pages, see [Edit source code](https://cactal.ai/docs/guides/edit-source-code.md)

**1. Write the form**

Use a literal action path and a visible label on every input. This baseline works without JavaScript: the endpoint redirects back to the page with `?submitted=contact`.

```tsx theme={null}
<form method="post" action="/_cactal/forms/contact" className="grid max-w-md gap-4">
  <label className="grid gap-1">
    Name
    <input name="name" autoComplete="name" required />
  </label>
  <label className="grid gap-1">
    Email
    <input name="email" type="email" autoComplete="email" required />
  </label>
  <label className="grid gap-1">
    Message
    <textarea name="message" rows={5} required />
  </label>
  <button type="submit">Send message</button>
</form>
```

**2. Enhance it with fetch**

Submit with `fetch` and `Accept: application/json` so visitors keep what they typed when something fails, and show a pending state, a thank-you, and field errors.

```tsx theme={null}
onSubmit={async event => {
  event.preventDefault()
  const form = event.currentTarget
  const response = await fetch(form.action, {
    method: 'POST',
    headers: { Accept: 'application/json' },
    body: new FormData(form)
  })
  const result = await response.json()
  if (result.ok) {
    // replace the form with a thank-you
  } else {
    // result.error.message, and result.error.fields[name] per input
  }
}}
```

`429` means the visitor should retry shortly; `403` with code `limit_reached` means the site used its Free plan allowance for the month.

**3. Test on the draft preview**

Submit the form on the draft host. Draft submissions are stored as test submissions and never count toward limits.

```bash Read test submissions theme={null}
curl -G 'https://api.cactal.ai/v1/websites/V1StGXR8_Z5jdHi6B-myT/forms/submissions' \
  -H "Authorization: Bearer $CACTAL_API_KEY" \
  --data-urlencode 'source=draft'
```

```json Response theme={null}
{
  "items": [
    {
      "id": "sub_7Hk2",
      "formName": "contact",
      "status": "inbox",
      "source": "draft",
      "data": [
        { "name": "name", "value": "Ada" },
        { "name": "email", "value": "ada@example.com" },
        { "name": "message", "value": "Testing the form" }
      ],
      "submitterEmail": "ada@example.com",
      "submitterName": "Ada",
      "pagePath": "/contact",
      "createdAt": "2026-09-11T14:02:11.000Z"
    }
  ],
  "nextCursor": null
}
```

**4. Choose who gets the email**

Each form emails its own recipients, starting with the organization owners. Add the business address to the form; addresses outside the organization confirm by email before they receive anything.

```bash Add a recipient theme={null}
curl -X POST 'https://api.cactal.ai/v1/websites/V1StGXR8_Z5jdHi6B-myT/forms/contact/recipients' \
  -H "Authorization: Bearer $CACTAL_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{ "email": "hello@northwinddental.com" }'
```

The notification subject uses the form name from the action path, so `quote-request` reads as "Quote Request".

## Next steps

* Export submissions as CSV from the **Forms** tab, or filter the API by `form`, `status`, and `source`.
* Restore a false positive from the **Spam** folder with **Not spam**; the missed notification is sent.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.