docs
stormdoor.dev
GATE

/v1/spam

A rule-based spam signal layer over free text — links, invisible characters, mixed-script evasion, shouting and known spam phrasing.

METHOD
POST
BODY CAP
16 KiB
CHECKS
10
FINDING CODES
8

POST /v1/spam runs a free rule-based layer over a block of text — a comment, a form submission, a chat message — looking for common spam and evasion signals. This is the free layer only (D-79); a model-backed classifier is a later, paid addition on top of the same route.

Request#

{ "text": "check this out https://a.example http://b.example http://c.example", "languages": ["en"] }
Field Type Required Notes
text string yes Bounded only by this gate’s 16 KB request-body cap (D-80) — no separate limit here. That’s roughly 15,000 characters of English text; fewer for text with accents or emoji.
languages array of strings no ISO 639-1 codes, up to 10 — same field as /v1/profanity. Decides which language’s spam.known_phrase list applies and unlocks text.unexpected_language.

No redact field — this gate scores properties of the message as a whole rather than finding specific values worth masking.

Checks this gate runs#

Check What it does
spam.link_density Fires when a message is mostly links — see spam.link_density.
spam.invisible_characters Zero-width and bidi-control characters — see spam.invisible_characters.
spam.mixed_script A single word mixing Latin with look-alike Cyrillic/Greek letters — see spam.mixed_script.
spam.shouting An almost entirely capital-letters message — see spam.shouting.
spam.known_phrase Matches curated spam/scam phrasing — see spam.known_phrase.
spam.sales_pitch Cold sales-pitch templates aimed at site owners, English only — see spam.sales_pitch.
spam.off_platform_contact An invitation to continue on Telegram, WhatsApp, Signal or Skype — see spam.off_platform_contact.
text.language Identifies the dominant language, when confident. Producer only — emits no finding.
text.unexpected_language Fires when the text is confidently written in a language outside your declared languages. Only runs when you send languages.

Every check here is pure rule-based text scanning and rune classification, plus the same compiled-in trigram language detector /v1/profanity uses — no model call, no network — so every rule check here is free on every plan (design rule 4).

Thresholds are deliberately conservative#

Every finding here moves the verdict (design rule 7), so each check is tuned to avoid firing on ordinary text: a single link never counts as link spam, a handful of capitalised words never counts as shouting, and mixed scripts only matter within one word, never across a whole sentence. See each finding’s own docs page for its exact thresholds and the reasoning behind them.

Example#

curl -s -X POST https://api.stormdoor.dev/v1/spam \
  -H "Authorization: Bearer sk_live_..." \
  -H "content-type: application/json" \
  -d '{"text":"CLICK HERE NOW http://a.example http://b.example http://c.example","languages":["en"]}'
{
  "ok": true,
  "verdict": "suspect",
  "findings": [
    { "code": "spam.link_density", "severity": "low", "message": "message is mostly links rather than text" },
    { "code": "spam.shouting", "severity": "low", "message": "message is almost entirely capital letters" }
  ],
  "data": { "matches": [] },
  "checks_run": ["spam.invisible_characters", "spam.known_phrase", "spam.link_density", "spam.mixed_script", "spam.shouting", "text.language", "text.unexpected_language"],
  "checks_skipped": [],
  "meta": { "gate": "spam", "ruleset": "reference-..." }
}

See response format for how checks_skipped reasons work, and don’t proxy that array — or the raw match offsets — to your own end users.

Finding codes#

Each code links to its own page with what it looks at, why it matters and what to do.

spam.invisible_charactersmediumspam.invisible_characterssuspect
spam.known_phraselowspam.known_phrasesuspect
spam.likely_spammediumspam.classifiersuspect
spam.link_densitylowspam.link_densitysuspect
spam.mixed_scriptmediumspam.mixed_scriptsuspect
spam.off_platform_contactlowspam.off_platform_contactsuspect
spam.sales_pitchmediumspam.sales_pitchsuspect
spam.shoutinglowspam.shoutingsuspect