All docs
4 min read

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:

  1. Flip status from spam to received.
  2. Move it to the inbox.
  3. Fire any pending notifications and the autoresponder, as if it had landed clean originally.
  4. 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