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
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 |
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
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
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
}