# Spam Protection

New forms start protected. You can adjust each check, and add human verification
when a form needs it. Open a form, then **Settings**.

## Checks That Are Already On

| Check | What it does | New form default |
| --- | --- | --- |
| Hidden field | Bots often fill a field people can't see. A filled field rejects the submission | On |
| Minimum submit time | Rejects a form sent faster than a person could fill it | 1 second |
| Maximum links | Rejects a submission with too many links | 3 links |

Set a time or link limit to `0` to turn that check off. These checks run on the
server, before anything is stored or delivered. A rejected submission returns
`SPAM_REJECTED`, and no email goes out.

Public routes also have a rate limit, so a burst of requests returns
`RATE_LIMITED`. Formerie sets that one for you.

## Submission Limits

Every form comes with sensible limits. To change them for one form, open
**Settings**, then **Submission limits**. The server enforces them, and **Use the
defaults** brings them back.

| Limit | What it does | Default |
| --- | --- | --- |
| Submissions per visitor | Keeps one visitor from flooding the form, over 1 minute, 10 minutes, 1 hour, or 1 day | 10 every 10 minutes |
| Submissions for the whole form | A ceiling across all visitors, over the same choice of times | 300 every hour |
| Maximum accepted submissions | A quota for the whole form, per day, week, or month, or in total. Once it's reached, the form stops accepting submissions | None |
| Ignore repeat sends of a submission for | A retry of the same submission in this time isn't counted or delivered twice. At least 60 seconds | 300 seconds |

Going over a rate limit returns `RATE_LIMITED`, a full quota returns
`FORM_QUOTA_REACHED`, and a retry returns `DUPLICATE_SUBMISSION`.

Hosted pages and script embeds mark their retries for you, and your own UI can send
`metadata.idempotencyKey` to do the same. Someone who fills in the form again is
sending a new submission. Visitors are told apart by a hashed network address, so
people on the same network share a per-visitor limit.

## Human Verification

Human verification is optional, and off on new forms. Turn it on form by form
under **Human verification**.

| Option | Good for |
| --- | --- |
| Formerie-managed Cloudflare Turnstile | Forms on supported Formerie-hosted domains, with no keys to set up |
| Your own Turnstile keys | Custom domains, or your own challenge settings |
| Your own reCAPTCHA v2 keys | A visible Google challenge |
| Your own reCAPTCHA v3 keys | A score, with your own minimum and expected action |

With your own keys, add every hosted or embedded domain to the widget's settings
in the provider's console. Your secret key is encrypted before storage, and the app
never shows it again.

Hosted pages, script embeds, and the browser mount render the challenge for you.
Custom UIs pass the provider's token as `verificationToken`:

```ts
await forms.submit("example.com", "contact", {
  fields,
  verificationToken: captchaToken,
});
```

Formerie verifies the token on the server before it accepts the submission.

## Allowed Origins

Add your site's origins under **Allowed origins** when you use an API or SDK
integration. Browser requests from other origins are refused.

## Not Available Yet

- One response per person.
- AI spam filtering, which is listed on the AI page as coming soon.
