Skip to content
On this page

Spam filtering and interface specifications

Verify genuine support intent and integrate decision or custom filtering APIs.

OnFire filters for genuine support communication, not just conventional spam. Reject promotions, sales or partnership outreach, phishing, unsolicited bulk mail, automated noise, gibberish, meaningless filler and unrelated messages. A genuine problem report, question, complaint, feature request or meaningful product feedback is valid even when brief, emotional or quoting malicious content as evidence. For existing tickets, relevant follow-up, requested details, resolution confirmation and thanks are valid. A ticket marker by itself is not evidence of genuine intent.

Configuration

Apply migrations 0034_decision_spam.sql and 0035_genuine_support_label.sql. Add a TypeSafe, Hugging Face or Decision HTTP credential to AI Spam Filter in AI function routing, then select AI decision model in global or tenant Spam Filter settings. Keys are encrypted; routes inherit product → tenant → system and support ordered failover. No model is enabled automatically.

The gate applies before new customer tickets and customer replies are inserted, including web/API submissions and inbound email. Internal staff notes and replies are outside this customer intake gate. Disabled spam settings skip the gate.

Both adverse probabilities are evaluated: score = max(spamProbability, 1 - supportProbability). A message can pass only when spamProbability < 0.5 and supportProbability > 0.5 and the score is below the suspect threshold. The configured thresholds default to spam 0.9 and suspect 0.5; the former separates clear rejection from uncertain rejection, not rejection from admission. Both spam and suspect are blocked. Email goes into the existing quarantine for manual review/release. Web/API clients receive HTTP 400 without a ticket or reply being created. Exhausted routes, invalid model responses and verification outages hold email for review and return HTTP 503 to web/API clients.

Review and restore filtered inputs

Apply 0036_filtered_inputs.sql before deploying this feature. System and tenant spam settings show inputs filtered in the last 30 days, including email quarantine and retained web/API tickets and replies. Administrators with spam.config can review only their live product/tenant scope; preview mode is read-only. Review preserves original text, submitted fields and filtering evidence. Remote images load only when explicitly opened and send no referrer.

To restore a false positive, enter a review reason. New email also requires an explicit product ticket type. New requests become tickets; matching customer replies return to their original open ticket. Restoration bypasses the filter decision only and rechecks customer identity, form/type validity, routing and translation. Repeated restoration returns the existing result. Older discarded web/API content cannot be recovered from before this migration.

Decision API

A commercial or self-hosted decision endpoint must implement onfire-decision-v1. Save its complete public HTTPS prediction URL under Decision HTTP; no suffix is appended. Its contract is distinct from the custom verdict API below.

OnFire sends POST with Authorization: Bearer <key>, Content-Type: application/json and Accept: application/json.

JSON
{ "protocol": "onfire-decision-v1", "model": "support-intent-v1", "message": { "kind": "reply", "subject": "Re: Cannot sign in", "content": "Thanks, that fixed it.", "fromEmail": "[email protected]", "toEmail": "[email protected]", "ticketContext": { "subject": "Cannot sign in", "latestReply": "Please reset your password and try again." }, "spfResult": "pass", "dkimResult": true }}
Request fieldContract
protocolExact value onfire-decision-v1
modelModel ID selected in the route
message.kindnew or reply
message.subject, message.contentOriginal text, without silent truncation
message.fromEmail, message.toEmailMay be empty for email-less or web/API customers
message.ticketContextOptional, validated product-owned ticket subject and latest public reply; never internal notes
message.spfResult, message.dkimResultOptional email authentication evidence

The endpoint must treat message and context text as untrusted evidence, never as instructions. It returns HTTP 2xx with application/json and exactly these fields (usage is optional):

JSON
{ "model": "support-intent-v1.2", "spamProbability": 0.01, "supportProbability": 0.98, "usage": { "promptTokens": 120, "completionTokens": 0, "totalTokens": 120 }}
Response fieldContract
modelNonempty resolved model/version, at most 200 characters
spamProbabilityRequired finite number in [0, 1]; probability of unsolicited spam/promotion/phishing/noise
supportProbabilityRequired finite number in [0, 1]; probability of genuine support communication under the policy above
usageOptional nonnegative safe integer promptTokens, completionTokens, totalTokens

The two probabilities are independent judgments; they need not sum to one. A low spam probability alone is insufficient: meaningless or unrelated content must also receive a low support probability. Missing support evidence, numeric strings, free text and verdict-only responses fail validation and trigger fallback.

Jev uses its native TypeSafe System One API with two independent Noul questions for spam and genuine support. It does not need an adapter for this contract.

Classification

Hugging Face uses the native text-classification payload: inputs is a JSON string containing the same contextual message fields, and parameters requests function_to_apply: "softmax" and top_k: 256. Configure an owner/model ID and token. Leave the URL empty for HF Inference, or provide the exact prediction URL of a dedicated or self-hosted endpoint compatible with this API.

The response is an array of { "label": "...", "score": 0.0 }, optionally wrapped in one array for one input. Configure both Spam label and Genuine support label using exact, distinct model labels. All class probabilities must be present, finite and in [0, 1], with unique labels and a total within 0.01 of one. Unrelated/meaningless classes remain separate from genuine support and contribute to 1 - supportProbability even when the spam label is unlikely.

A generic spam/ham, sentiment or toxicity classifier does not establish genuine support intent. Use a model trained for these classes and this contextual input; OnFire does not assume that ham or LABEL_0 means a real ticket. Token usage is not reported by this interface; usage logs retain the request with zero reported tokens. Native protocols other than these require a Decision HTTP adapter.

Custom spam API

A custom verdict endpoint uses onfire-spam-v1, with the complete HTTPS URL from Spam Filter settings. OnFire sends POST with:

JSON
{ "authorization": "Bearer <secret>", "content-type": "application/json", "accept": "application/json", "x-onfire-spam-protocol": "onfire-spam-v1"}
JSON
{ "messageId": "<[email protected]>", "fromEmail": "[email protected]", "toEmail": "[email protected]", "subject": "Cannot sign in", "content": "My account is locked.", "kind": "new"}

Optional input fields are userIp, spfResult, dkimResult, autoSubmitted, precedence, listId, returnPath and ticketContext as described above. Ignore unsupported optional input fields. messageId and email fields may be empty for web/API intake. Response:

JSON
{ "verdict": "spam", "score": 0.97, "reason": "Unsolicited product promotion", "referenceId": "classification-123"}

verdict is required and must be allow, suspect or spam. Optional score is a number in [0, 1], reason has at most 1,000 characters and referenceId at most 500. Unknown response fields are rejected. Return allow only for genuine support communication; return spam for any non-ticket message and suspect when uncertain. Both non-allow verdicts are held rather than becoming tickets.

Limits and validation

Endpoints require public HTTPS port 443, no credentials in URLs and no redirects. Decision requests are limited to 24,000 UTF-8 bytes and responses to 64 KiB. Custom verdict requests cap content at 100,000 characters and responses at 64 KiB. The per-attempt timeout is 500–10,000 ms; fallback latency can accumulate across route entries. Errors never create invented scores or silently admit a message.

These are interface and admission rules, not a promise of perfect model accuracy. Evaluate labeled English/Chinese examples, promotions, meaningless text, genuine complaints, quoted spam and contextual replies with your chosen model.