# PostTo > Handle HTML form submissions without a backend. PostTo routes form submissions to email — with AI spam
filtering, rate limiting, CORS handling, and a full management dashboard. No SPF, DKIM, or email infrastructure
required. Hosted in Europe, GDPR-native. ## What PostTo Does PostTo is a form backend service for developers. Instead of
building email delivery, spam filtering, and submission management from scratch, developers point their HTML form's
`action=""` attribute at a PostTo endpoint URL. PostTo handles everything else. Integration requires no SDK, no
JavaScript library, and no changes to how the form looks. Any HTML form, any framework, any static site can use PostTo.
Full public documentation is available with no account required — start at [https://postto.dev/docs](https://postto.dev/docs).
## Common Use Cases - Contact forms on static sites (Hugo, Astro, Next.js, plain HTML) - Feedback forms and bug-report
widgets - Lead capture and newsletter sign-up forms - Job application forms - Client inquiry forms on agency or
freelancer sites - Any form that should deliver to an inbox without a custom backend ## How PostTo Compares PostTo is a
direct alternative to Formspree, Basin, Netlify Forms, and similar form backend services. Key differences: - **Hosted in
Europe** — GDPR-native; data stays within European borders - **Full submission dashboard** — every submission stored,
searchable, exportable; spam-flagged submissions are held (never silently dropped) and can be reviewed and released -
**Signed API mode** — server-side integrations can HMAC-SHA256-sign each request for replay protection; no other service
in this space offers this natively - **Encryption at rest** — submission fields (PII) encrypted with AES-256 - **Webhook
fan-out** — forward submissions to your app, CRM, or Slack via signed outbound webhooks - **Tiered AI spam filtering** —
first-party baseline filtering on all plans; optional AI-powered classification (off by default, opt-in per endpoint)
included on Pro/Scale or as a $5/mo add-on on
Starter Detailed comparison pages: - [Formspree alternative](https://postto.dev/alternatives/formspree) - [Netlify
Forms alternative](https://postto.dev/alternatives/netlify-forms) - [Basin alternative](https://postto.dev/alternatives/basin)
- [Forminit alternative](https://postto.dev/alternatives/forminit) - [Web3Forms alternative](https://postto.dev/alternatives/web3forms)
AI app builder guides: - [Lovable contact form](https://postto.dev/ai-builders/lovable) - [Bolt.new contact form](https://postto.dev/ai-builders/bolt-new)
- [v0 contact form](https://postto.dev/ai-builders/v0) - [Replit Agent contact form](https://postto.dev/ai-builders/replit)
- [Base44 contact form](https://postto.dev/ai-builders/base44) Framework integration guides: - [Astro contact form](https://postto.dev/guides/astro)
- [Hugo contact form](https://postto.dev/guides/hugo) - [Next.js form to email](https://postto.dev/guides/nextjs)
- [WordPress contact form](https://postto.dev/guides/wordpress) - [Laravel contact form](https://postto.dev/guides/laravel)
- [SvelteKit contact form](https://postto.dev/guides/sveltekit) - [Nuxt contact form](https://postto.dev/guides/nuxt)
Problem / solution pages: - [GDPR form backend](https://postto.dev/solutions/gdpr-form-backend) - [HTML form to
email](https://postto.dev/solutions/html-form-to-email) - [Form spam filtering](https://postto.dev/solutions/form-spam-filtering)
- [Client website contact forms](https://postto.dev/solutions/client-website-contact-forms) Documentation pages
(technical reference, no account required): - [Getting started](https://postto.dev/docs) - [Sending submissions](https://postto.dev/docs/sending-submissions)
- [Response format & error codes](https://postto.dev/docs/response-format) - [Field mapping](https://postto.dev/docs/field-mapping)
- [Notification language](https://postto.dev/docs/notification-language) - [Reserved fields](https://postto.dev/docs/reserved-fields)
- [Signed mode (HMAC-SHA256)](https://postto.dev/docs/signed-mode) - [Webhooks](https://postto.dev/docs/webhooks)
- [CORS & domain allowlist](https://postto.dev/docs/cors) - [Spam protection](https://postto.dev/docs/spam-protection)
- [Rate limits & quotas](https://postto.dev/docs/rate-limits-quotas) ## Integration Examples ### HTML form (browser)
```html
``` ### Signed API (HMAC-SHA256, server-side only) The signed mode adds a timestamp and HMAC-SHA256 signature to each
request, preventing replay attacks and forged submissions. The secret key must stay server-side — never expose it in
browser code. ```javascript const crypto = require('crypto'); const ts = Math.floor(Date.now() / 1000).toString(); const
body = JSON.stringify({ subject: 'Contact form', message: 'Hello, I would like to...', email: 'alice@example.com', name:
'Alice', }); const sig = crypto.createHmac('sha256', SECRET_KEY) .update(`${ts}.${body}`).digest('hex'); await fetch('https://postto.dev/api/v1/send/token',
{ method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'X-PostTo-Timestamp': ts,
'X-PostTo-Signature': sig, }, body, // exact bytes that were signed }); ``` PHP, Python, and Laravel examples are
available in the endpoint dashboard. ## Request Fields & Response Format Submit any field names — every field you send
is stored verbatim in the submission's `fields` object. Four field names are also read as **canonical roles** used to
build the outgoing email; they are not reserved or stripped, just given extra meaning if present: | Field name (default)
| Canonical role | |---|---| | `subject` | Email subject line | | `message` | Email body | | `email` (or `_replyto`) |
Reply-To address — discarded (not rejected) if not a valid email | | `name` | Sender name | If a form's fields are
already named `subject`/`message`/`email`/`name`, they resolve automatically — nothing is overwritten or ignored. If a
form uses different names (e.g. `topic`, `body`, `from`), map them once under the endpoint's **Settings → Field map** in
the dashboard so PostTo knows which field plays which role; every field is still stored under its original name in
`fields` regardless of mapping. Fields with no role just ride along in `fields` and appear in the dashboard and webhook
payload. Reserved field names with special (non-role) handling: | Field | Purpose | |---|---| | `_hp` | Honeypot — must
stay empty; a filled value is a silent discard, not stored, not delivered | | `cf-turnstile-response` | Cloudflare
Turnstile token, read only when Turnstile is enabled on the endpoint | | `_next` | Redirect URL after a successful
non-JSON (browser) submission; overrides the endpoint's configured success redirect | ### Response format PostTo
negotiates on the request's `Accept` header. Send `Accept: application/json` to get a JSON response back instead of a
browser-style redirect: Success (200): ```json { "id": "sub_01JXF...", "status": "pending" } ``` Error: ```json {
"error": "rate_limited", "message": "Too many submissions." } ``` `error` is always a stable machine code, never free
text: | Code | HTTP | Meaning | |---|---|---| | `endpoint_paused` | 403 | Endpoint is paused | | `endpoint_unverified` |
403 | Destination not yet verified | | `invalid_signature` | 401 | HMAC signature missing, invalid, or expired (signed
mode) | | `turnstile_failed` | 403 | Cloudflare Turnstile human verification failed | | `rate_limited` | 429 |
Per-endpoint or per-IP limit exceeded | | `quota_exceeded` | 402 | Monthly quota reached | | `empty_submission` | 422 |
No fields in the request body, or every field blank | | `origin_not_allowed` | 403 | Origin not in the allowlist | A plain `