Spam
The spam folder is where every flagged submission goes. Anything any layer of the spam stack catches — honeypot, rate limit, captcha-fail, custom rule, Akismet, AI moderation — ends up here, not in the inbox.
We send a 200 back to the submitter regardless, so bots don't learn what bypasses what. From the form's POSTer's perspective, spam looks identical to success.
The list
Same shape as the inbox — timestamp, form, payload preview, status pill — plus one extra column: reason.
The spam_reason column is a short string telling you which layer flagged it:
| Reason | Layer | What it means |
|---|---|---|
honeypot |
Honeypot | The hidden trap field was filled. Almost always a bot. |
rate_limit |
Rate limiter | Too many submissions from one IP in too short a window. |
captcha_failed |
Captcha | hCaptcha or reCAPTCHA returned invalid. |
rule:blocked_email:{email} |
Custom rules | Sender email matched your blocked_emails list. |
rule:keyword:{kw} |
Custom rules | Payload contained one of your keywords. |
rule:regex |
Custom rules | A regex in your regex list matched. |
akismet |
Akismet | Akismet's classifier said yes. |
ai:{category} |
AI moderation | Pro+ AI flagged it under a category — crypto, solicitation, harassment, etc. |
You'll see this in the row, in the detail panel, in the API, in MCP. Same string everywhere.
Why something landed here
Click a row to open the detail panel. The metadata section calls out the spam reason and shows the exact payload — including any honeypot field that was filled, so you can see what tipped us off.
If a layer was confident, the row is in spam. If it wasn't, the row is in the inbox with an ai_moderation_score recorded but no spam status. The cutoff for AI moderation is configurable per form.
Recovering false positives
False positives happen — especially with custom rules or aggressive AI moderation. Two ways to fix:
Mark not spam
Click the row, then Mark not spam in the detail panel. We:
- Flip
statusfromspamtoreceived. - Move it to the inbox.
- Fire any pending notifications and the autoresponder, as if it had landed clean originally.
- Trigger any webhooks attached to the form.
The spam_reason is preserved on the row in case you want to audit later. The submission keeps its original timestamp.
Via the API: POST /submissions/{submission}/mark-not-spam. Via MCP: mark_not_spam.
Bulk recovery
Select multiple rows with the checkbox column, then Mark not spam in the bulk action bar. Capped at 500 rows per call — see Bulk actions →.
If you're recovering many false positives at once, that's a signal: revisit the rule that caught them. A custom rule with high false-positive rate is worse than no rule.
Permadelete
The spam folder isn't a holding pen — it's part of the same data store as the inbox. Spam counts toward your retention policy, gets backed up, costs the same.
If you don't want to keep it:
- Per-row — open the detail panel, hit Delete. Same as the inbox: permanent, no trash, files go too.
- Bulk — select rows, Delete in the bulk action bar.
- Auto-purge — under the form's Settings → Spam → Auto-purge, enable to delete spam rows older than 7, 30, or 90 days. Off by default.
For privacy-sensitive forms (anything in consent-required scope), auto-purge is recommended. There's no value in keeping spam from six months ago.
Reading the spam stack
The full pipeline diagram is on Submissions overview →. Quick reference:
honeypot → rate_limit → captcha → custom_rules → akismet → ai_moderation
Earlier layers are cheaper. Honeypot is free. AI moderation costs a token call per submission. We short-circuit at the first layer that flags — there's no "all layers always run" overhead.
What's next
- Custom rules → — write your own block patterns
- Submissions overview → — full pipeline diagram
- Bulk actions → — recover or delete in batches