Scanning API

The scanning endpoints classify email messages for spam and assign message classes. There are two modes: full classification (spam + message) and message-only.

POST /classify

Coming soon: Classifying by file path is not available yet. Use POST /classify-stream below to classify by streaming the raw message content.

Classify an email by file path. SpamFoo reads the file from disk, extracts signals, and returns a classification result.

Request

POST /classify
Content-Type: application/json

{
    "path": "/var/spool/mail/message.eml",
    "ipAddress": "203.0.113.50",
    "senderEmail": "sender@example.com",
    "userId": "user@domain.com"
}
Field Required Description
path Yes Absolute path to the .eml file
ipAddress No Sender's IP address (improves accuracy)
senderEmail No Sender's email address (improves accuracy)
userId No Recipient user ID (enables per-user personalization)

Response

HTTP/1.1 200 OK
Content-Type: application/json

{
    "isSpam": false,
    "spamProbability": 0.12,
    "classification": "primary",
    "textSpamScore": 0.08,
    "metaSpamScore": 0.15,
    "confidenceMargin": 0.76,
    "entropy": 0.4,
    "disagreement": 0.03,
    "shouldEscalate": false,
    "bypassReason": null,
    "senderAuthenticated": true
}
Field Type Description
isSpam boolean Whether SpamFoo considers the message spam
spamProbability float Spam confidence (0.0 = not spam, 1.0 = definitely spam)
classification string Message class: primary, promotions, updates, or transactions
textSpamScore float Score from the text analysis model
metaSpamScore float Score from the header/metadata model
confidenceMargin float Distance from the decision boundary (higher = more confident)
entropy float Uncertainty in the classification (lower = more certain)
disagreement float How much the text and meta models disagree
shouldEscalate boolean True if SpamFoo recommends human review (low confidence or conflicting signals)
bypassReason string or null If a rule overrode the model: user_rule, knn_memory, phishtank_match, or parse_error
senderAuthenticated boolean Whether the sender passed SPF/DKIM/DMARC authentication
Note: If your license includes classification but not spam detection, isSpam is always false and spamProbability is 0.0. Use the classification field to determine the message type.

POST /classify-stream

Classify an email by streaming the raw content in the request body. Metadata is passed via headers.

Request

POST /classify-stream
Content-Type: message/rfc822
X-Sender-IP: 203.0.113.50
X-Sender-Email: sender@example.com
X-User-ID: user@domain.com

[raw .eml content]
Header Required Description
X-Sender-IP No Sender's IP address
X-Sender-Email No Sender's email address
X-User-ID No Recipient user ID for personalization

The response format is the same as /classify. Messages up to 10 MB are buffered in memory; larger messages use a temporary file.

POST /tabs/classify

Coming soon: Classifying by file path is not available yet. Use POST /tabs/classify-stream to classify by streaming the raw message content.

Classify an email into a message class only, skipping spam detection. Use this when spam filtering is handled by another system and you only need classification of messages.

Request

POST /tabs/classify
Content-Type: application/json

{
    "path": "/var/spool/mail/message.eml",
    "userId": "user@domain.com",
    "senderEmail": "sender@example.com",
    "senderIp": "203.0.113.50"
}

All fields except path are optional. A stream variant is available at POST /tabs/classify-stream with the same headers as /classify-stream.

Response

{
    "classification": "promotions",
    "confidence": 0.92,
    "classificationProbabilities": {
        "primary": 0.05,
        "promotions": 0.92,
        "updates": 0.02,
        "transactions": 0.01
    },
    "personalizationSource": null,
    "elapsedMs": 18
}
Field Type Description
classification string Assigned class
confidence float Confidence in the assigned class (0.0 to 1.0)
classificationProbabilities object Per-classification probability distribution
personalizationSource string or null If a personalization rule was applied: tabs_rule or tabs_knn
elapsedMs int Processing time in milliseconds

POST /classify/diagnostic

Coming soon: The file-path form is not available yet. Use POST /classify/diagnostic-stream to run a diagnostic by streaming the raw message content.

Returns a detailed breakdown of how SpamFoo reached its classification decision, including per-model scores, signal extraction details, and timing per stage. Use this for troubleshooting, not in production mail flow.

POST /classify/diagnostic
Content-Type: application/json

{
    "path": "/var/spool/mail/message.eml"
}

The request format is the same as /classify. A stream variant is available at POST /classify/diagnostic-stream.

The response is a large JSON object containing email metadata, the final result, individual model inputs and outputs, URL scanner results, tabs classification, and a timing breakdown. The exact fields may vary between SpamFoo versions.

POST /classify/embed-text

Generate a 384-dimensional embedding vector for a text string. This uses the same embedding model SpamFoo uses internally for similarity matching.

POST /classify/embed-text
Content-Type: application/json

{
    "text": "Important account security update"
}
{
    "embedding": [0.0234, -0.1567, 0.0891, ...]
}

This endpoint is not queued and responds immediately.

Error Responses

Status Meaning
400 File not found, missing required fields, or unparseable email content
503 Classification queue timeout. The server is overloaded. Deliver the email without classification.

When SpamFoo returns 503, the response includes the current queue depth:

{
    "error": "Classification queue timeout",
    "queueDepth": 10
}