# Forms and leads

Checked against the code on 2026-10-30 (version 0.1.0).

A static site can receive form submissions without a backend. You mark the form in the HTML; SeeDropHost finds it when you deploy, receives the submissions and keeps them in a lead inbox.

## Mark the form

Either the native attribute:

```html
<form data-sdh-form="contact" method="POST" action="/thanks">
  <input name="name">
  <input name="email" type="email">
  <textarea name="message"></textarea>
  <button type="submit">Send</button>
</form>
```

or the attribute many sites already use (handy when migrating):

```html
<form name="contact" method="POST" data-netlify="true">
```

The form name is `data-sdh-form`, or `name` when `data-netlify="true"` (or a bare `netlify` attribute) is used. The deploy report lists the forms found (`report.forms`). Forms are detected in HTML files up to 2 MB.

## What happens when someone submits

1. The browser sends `POST` to any page of the site. When serving the page, SeeDropHost adds a hidden `form-name` field to each detected form, so you do not have to.
2. Bodies are accepted as `application/x-www-form-urlencoded` or JSON, up to 1 MB. Each value is cut at 10 000 characters. File uploads (`multipart/form-data`) are not supported.
3. The answer is a `303` redirect to the form's `action` when it is a path on the same site (for example `/thanks`; `//host` and `/\host` do not count), otherwise to the same page with `?enviado=1`. With `Accept: application/json` the answer is `{"ok":true}`.

## Antispam

- **Honeypot:** if the form declares one (`data-sdh-honeypot="field"` or `netlify-honeypot="field"`), that field is the trap. If it does not, SeeDropHost injects its own hidden field when serving the page. A filled honeypot marks the submission as spam.
- **Links:** more than 3 `http(s)://` links in one submission marks it as spam.
- **Rate limit:** 10 submissions per minute per IP per site; above that, `429` with `Retry-After: 60`.
- Spam gets the same success answer, so senders do not learn how to get around it. It is kept apart and hidden by default.
- IP addresses are not stored: only a keyed hash (HMAC-SHA256, per site, with a key derived from the server's master key), so the address cannot be recovered by trying every IP.
- Anonymous drops do not accept submissions (`403`) until they are claimed.

## Read the leads

```bash
# API
curl -H "Authorization: Bearer $SDH_TOKEN" "$SDH_API_URL/v1/sites/my-site/submissions?form=contact"
# CLI
npx sdh leads my-site --form contact
```

From an agent: the MCP tools `list_forms` and `list_leads`. The dashboard shows them too. Add `spam=1` to the API call to include spam.

## Not available yet

- Email or webhook notification of new leads.
- CSV export (the API returns JSON).
- CAPTCHA.
