Antispam & Antiphishing
The antispam API inspects a message (SMS, WhatsApp, email) and returns a verdict — clean, spam, or phishing — with a severity score, a plain-language explanation, and the URLs it found. Use it to block, quarantine, or flag messages before they reach your users.
POST /api/v1/antispam — requires an API key with the antispam scope. The full request and
response are in the interactive API Reference;
this page explains how to read the result.
Authentication
X-Tenant-Id: <your tenant id>
X-Api-Key: <your API key>
Sync vs. async (polling)
By default the check is synchronous — the verdict is in the response. You can also run it asynchronously: the call returns immediately with a job id you poll (or receive via webhook), so you never block on a longer check.
Opt into async any of these ways:
- query —
POST /api/v1/antispam?async=true - header —
Prefer: respond-async - body —
{ "async": true }
Requests that include attachments are always asynchronous — each attachment is deep-scanned (which can take several seconds), so the call never blocks on a verdict.
An async request returns 202:
{ "status": "queued", "jobId": "…", "filescan_job_ids": ["…"] }
Poll for the result:
curl -s https://shield.orbis.roundbox.dev/api/v1/antispam/JOB_ID \
-H "X-Tenant-Id: $TENANT_ID" -H "X-Api-Key: $API_KEY"
# → { "status": "queued|running|done|failed", "result": { …verdict… }, "error": null }
When status is done, result has the same shape as the synchronous response, with each attachment's scan outcome under result.filescan[]. If you've configured a webhook, it also fires on completion with that payload. Each attachment is a separate file-scan (own quota/price) and is individually pollable via GET /api/v1/files/{jobId}.
Reading the response
{
"inspectionId": "26bc4f0d-ac21-407b-bf21-bb452cd17319",
"verdict": "phishing",
"score": 46,
"modelAvailable": true,
"rationale": "Dangerous: this message uses a shortened link and pushes you to “verify” a delivery, a common scam. Do not click it; go to the delivery company’s official app instead.",
"urls": ["http://bit.ly/verify-now"],
"lang": "en",
"elapsedMs": 6884,
"layers": [ "…per-layer evidence…" ]
}
verdict — the decision
verdict is the authoritative classification and the field your integration should act on:
verdict | Meaning | Typical action |
|---|---|---|
clean | No spam or phishing indicators | Deliver normally |
spam | Unsolicited / bulk, low targeting | Quarantine or route to junk |
phishing | Credential theft, impersonation, or a malicious lure | Block and alert |
score — the severity
score is 0–100 and expresses how strong the signal is. Use it to prioritise or sort —
not to derive the category (that's verdict).
score | Reading |
|---|---|
| 0–24 | Low |
| 25–59 | Elevated |
| 60–100 | High |
Because a strong indicator can drive a phishing verdict even at a modest score, you may see
verdict: "phishing" with a mid-range score. Always act on verdict; treat score as
confidence.
Other fields
rationale— a plain-language, safe-to-display explanation of the verdict.urls— URLs and hosts found in the message (including any decoded from QR codes), each checked against ROUNDBOX threat intelligence.modelAvailable—truewhen the full AI analysis was applied.elapsedMs— how long the check took, end to end, in milliseconds.layers— per-layer evidence behind the verdict: each entry has a layer id, a 0–100 contribution, and human-readable signal strings you can log or display.
The antispam response never includes any pricing or cost information.
Offensive-language filtering (opt-in)
Set "checkOffensiveLanguage": true on the request (or tick the box in the console) to also screen the message for offensive / foul language — in English, Spanish, and Central-American Spanish. Off by default.
When offensive language is detected, the response is blocked:
{
"verdict": "clean",
"blocked": true,
"blockReason": "offensive-language",
"offensive": {
"severity": "high",
"categories": ["profanity", "insult"],
"matchedCount": 2,
"maskedRationale": "Message blocked for offensive language (profanity, insult)."
}
}
blocked/blockReasonare separate fromverdict— a message can beverdict: "clean"(not spam or phishing) yetblocked: truefor offensive language.- The
offensiveobject is safe to show end users: severity, category labels, and a match count. ThemaskedRationalenever reprints the offending words — they are always masked. - Add your own blocked words under Account settings → Offensive Language; they apply on top of the built-in dictionaries.
Scanning attachments for malware
The antispam endpoint inspects the message body and its URLs. To scan files for malware — with antivirus signatures, heuristics, and (on premium plans) behavioral sandboxing, including encrypted archives — use the dedicated file-scan endpoint:
POST /api/v1/files— scan a file (requires thefilescanscope). See File Inspection.
Test it with EICAR
EICAR is the industry-standard benign antivirus test file — safe to use, and every scanner flags it — so it's the right way to prove detection end to end, including inside a password-protected zip.
# 1. Create the EICAR test file
printf '%s' 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' > eicar.com
# 2. Wrap it in a password-protected zip ("infected" is a recognised test password)
zip --password infected eicar.zip eicar.com
# 3. Submit it (force=true skips any cached verdict so the engines actually run)
curl -s https://shield.orbis.roundbox.dev/api/v1/files \
-H "X-Tenant-Id: $TENANT_ID" \
-H "X-Api-Key: $API_KEY" \
-F "file=@eicar.zip" \
-F "password=infected" \
-F "force=true"
# → { "status": "pending", "jobId": "job-abc123" }
Then poll for the verdict:
curl -s https://shield.orbis.roundbox.dev/api/v1/files/job-abc123 \
-H "X-Tenant-Id: $TENANT_ID" \
-H "X-Api-Key: $API_KEY"
# → { "status": "done", "verdict": "malicious", … }
Shield opens the archive and scans the file inside it, returning a malicious verdict for
EICAR. A plain eicar.com works too — just send it as file with force=true.