# Formchan form integration specification

Give this document and your dedicated submission URL from the Formchan dashboard to your coding AI.
Formchan accepts contact form submissions, stores them in your inbox, and handles AI triage and notifications.
This specification covers integration on your website.

## Instructions for the coding AI

1. Use the dedicated submission URL supplied by the user as `FORM_ACTION_URL`. Never invent a form key or ship `YOUR_FORM_KEY` as a real value. Ask for the URL if it is missing.
2. Inspect the existing website, form design, required fields and accessibility. Make the changes needed to connect the form.
3. If there is no form, build one that matches the website.
4. Submit directly from the browser. No custom submission server, API key or authentication header is needed just to connect to Formchan.
5. Implement a sending state, prevent duplicate submissions, and display success and error states. Preserve inputs after an error so the user can retry.
6. Test from the actual website and confirm the submission appears in the Formchan inbox. Changing billing plans or notification settings is outside this integration task.

## Endpoint and request

- Method: `POST`.
- URL: the dedicated URL from the dashboard, for example `https://YOUR_FORMCHAN_HOST/f/YOUR_FORM_KEY`.
- Authentication: no login or authorization headers are needed for submission to the dedicated URL.
- Formats: URL-encoded HTML forms, `FormData`, or a JSON object.
- For JSON responses: set `Accept: application/json`.
- With `FormData`, do not set `Content-Type` yourself. The browser sets the multipart boundary.
- With JSON: set `Content-Type: application/json` and use `JSON.stringify({...})`.
- File uploads are not supported. Use text fields.

## Fields

| Field name | Purpose |
| --- | --- |
| `name` | Sender's name |
| `email` | Email address used for replies |
| `message` | Inquiry text |
| Custom names, e.g. `company`, `subject` | Additional fields displayed in the inbox and notification emails |
| `_gotcha` | Hidden honeypot field; human visitors should leave it empty |
| `_next` | Optional destination URL after an HTML submission |

Aliases such as `mail` and `_replyto` for email, and `fullname` for the name, are recognized. Prefer `name`, `email` and `message` for a new form.
Completely empty submissions fail. Make email required on the website when a reply address is needed.

Keep requests within 64KB, 50 fields and 5,000 characters per value. Fields after the first 50 and characters beyond 5,000 are not saved.

## Minimal HTML form

Replace `FORM_ACTION_URL` with the actual dedicated URL.

```html
<form id="contact-form" action="FORM_ACTION_URL" method="POST">
  <label>Name <input name="name" autocomplete="name"></label>
  <label>Email <input name="email" type="email" autocomplete="email" required></label>
  <label>Message <textarea name="message" maxlength="5000" required></textarea></label>
  <input name="_gotcha" style="display:none" tabindex="-1" autocomplete="off" aria-hidden="true">
  <button type="submit">Send</button>
</form>
<p id="form-status" role="status" aria-live="polite"></p>
```

A regular HTML submission shows the Formchan confirmation page. JavaScript is optional.

## JavaScript example for an inline confirmation

```js
const form = document.getElementById('contact-form');
const status = document.getElementById('form-status');
let sending = false;

form.addEventListener('submit', async (event) => {
  event.preventDefault();
  if (sending) return;
  sending = true;
  const button = form.querySelector('button[type="submit"]');
  button.disabled = true;
  status.textContent = 'Sending…';
  try {
    const response = await fetch(form.action, {
      method: 'POST',
      headers: { Accept: 'application/json' },
      body: new FormData(form),
    });
    const data = await response.json();
    if (!response.ok || data.ok !== true) throw new Error(data.error || 'send_failed');
    status.textContent = 'Sent. Thank you for getting in touch.';
    form.reset();
  } catch {
    status.textContent = 'Could not send. Please try again later.';
  } finally {
    sending = false;
    button.disabled = false;
  }
});
```

Use the same request contract in React or another framework, with native form validation and the framework's existing state management.

## JSON responses

Success: HTTP 200, `{"ok":true}`.
Failure: `{"ok":false,"error":"error_code"}`.

| HTTP | error | Meaning |
| --- | --- | --- |
| 404 | `not_found` | Form does not exist or has been archived |
| 403 | `origin` | Sending website is not allowed |
| 429 | `rate` | Short-term submission rate limit reached |
| 400 | `body` | Request could not be parsed or exceeds the size limit |
| 400 | `empty` | All submission fields are empty |

A success response means the submission was accepted. AI triage and notifications run after the response. Do not wait for AI processing in the frontend or expect summary, tags or importance in the response.

## Redirect after submission

For regular HTML submission, add this field to use your own confirmation page:

```html
<input type="hidden" name="_next" value="https://YOUR_SITE/thanks">
```

Use an HTTP or HTTPS URL. If allowed origins are configured, the target must belong to an allowed origin. `_next` does not redirect a JSON response; handle any navigation in the frontend when using fetch.

## Allowed origins and spam protection

- Allowed websites can be configured in the form settings. Once configured, other origins and requests without an Origin header are rejected.
- Register development origins, e.g. `http://localhost:5173`, and production origins, e.g. `https://YOUR_SITE`, as needed.
- Browser CORS requests and OPTIONS are supported. No credentials or custom headers are needed.
- A populated `_gotcha` field returns success but does not save an inquiry. Leave it empty when testing.

## Platform notes

- HTML: set action to the dedicated URL and method to POST.
- WordPress: custom HTML blocks work. Existing form plugins may have their own submission handlers; check whether the destination can be changed or adapted.
- Webflow: set Action to the dedicated URL and Method to POST.
- React / Next.js: use fetch with FormData or JSON. You do not need a new backend solely to submit the form.

## Verification checklist

- All dedicated URL placeholders have been replaced with the actual URL.
- Every input has a label, and the form works with the keyboard.
- Required fields and email validation work.
- Duplicate submissions are prevented while sending.
- Success and error messages appear, and errors preserve input values.
- A submission from the actual website appears in the Formchan inbox.
- Both development and production origins are allowed.
- If notification recipients have been configured and verified, check notifications too. Avoid bulk test submissions or notifications to nonexistent addresses.
