# Rate limits

Each (tenant, IP) pair gets 60 requests per 60 seconds using a fixed window. Exceed that and you get a `429` with a `Retry-After` header.

IPv4 addresses are truncated to /24 and IPv6 to /48 before they enter the rate limiter, so a NAT or shared network doesn't unfairly penalize a single user.

## Spam protection

- **Body size.** Requests over 64 KB are refused with `413 PAYLOAD_TOO_LARGE`.
- **Wrong keys.** An address that sends 10 wrong API keys is refused with `429` for about a minute. Valid keys are never counted.
- **Allowed websites.** List your sites on the endpoint (for example `https://example.com`). Browser submissions from any other site, and requests that do not say where they came from, get `403 ORIGIN_NOT_ALLOWED`. Leave the list empty for server-to-server calls.
- **Cloudflare Turnstile.** Paste your own Turnstile secret key (free at Cloudflare) on the endpoint and add the Turnstile widget to your form. Each submission must then carry the solved token in the `cf-turnstile-response` field or the `x-turnstile-token` header, or it gets `403 CAPTCHA_FAILED`.
- **Honeypot.** A hidden `website` field that people leave empty. Bots that fill it get a fake success and nothing is stored.
- **Confirmation emails.** One address gets at most 3 confirmation emails a day from your workspace, and invalid addresses get none.
- **Plan email limits.** Each plan has a daily and a monthly email ceiling, and all workspaces share one sending capacity. When either is reached, the email is held, and the submission is still saved and shown in your dashboard.
