Developer docs

Documentation

Guides and API reference for running AntiBait analysis from the web app or from your own code.

API reference

Full AI analysis

Premium

Full analysis of one raw email: deterministic checks, an AI verdict that is reviewed again when it disagrees with the evidence, and the findings behind the result.

API endpoint

POST
POST/api/analysis/advanced

Request body

{
  "email": "string 
  (full raw email)"
}

Max 1MB. Must include headers and body.

Response

{
  "status": 200,
  "success": true,
  "analysisId": "uuid",
  "cached": false,
  "score": number,
  "headerScore": number | null,
  "bodyScore": number | null,
  "threatType": string | null,
  "type": string | null,
  "confidence": number | null,
  "tags": string[],
  "reasoning": string | null,
  "falsePositiveChecks": string[],
  "error": string | null,
  "data": object,
  "senderInsights": {
    "status": "ready" | "locked" | "off" | "unavailable",
    "since": string | null,
    "insights": [{ "code": string, "severity": string, ... }]
  }
}

Analysis features

AI-powered detection

A language model judges the message with the deterministic findings in front of it; a second pass reviews verdicts that disagree with the evidence

Comprehensive scoring

Multi-dimensional risk assessment with confidence metrics

Threat categorization

Detailed tags and threat type classification

Detailed reasoning

The model's explanation plus the evidence: sender identity, authentication, links, attachments and how the verdict was reached

Response fields

statusnumber

HTTP status code (always 200 for successful requests)

successboolean

Indicates if the analysis was successful

analysisIdstring | null

Unique identifier for this analysis (UUID format)

cachedboolean

Whether the result was retrieved from cache (previously analyzed email)

scorenumber

Overall risk score (0-100) combining all analysis factors

headerScorenumber | null

Always null on this endpoint. The header heuristic score is in data.enhancedAnalysis.headerScore

bodyScorenumber | null

Always null on this endpoint. The body heuristic score is in data.enhancedAnalysis.bodyScore

threatType / typestring | null

Classification: "phishing", "malicious", "spam", "legitimate", or null

confidencenumber | null

Confidence (0-100) in the verdict. A result resolved without a model reports 95

tagsstring[]

Array of threat indicators detected (e.g., ["SUSPICIOUS_LINK", "URGENT_LANGUAGE"])

reasoningstring | null

The model's explanation. A result resolved without a model (data.triage.gateApplied) carries a short note starting with "Resolved without AI" instead

falsePositiveChecksstring[]

What a reviewer should check before trusting the verdict, including the identity check's advice when a brand/domain mismatch kept the score at 50 or above

errorstring | null

Error message if analysis failed, null on success

dataobject

The full verdict object without token counts: details (subject, from, to, date, replyTo, returnPath), llmModel ("triage-gate" when no model ran), enhancedAnalysis, triage and cache

data.enhancedAnalysisobject

Deterministic evidence gathered before the model runs: headerScore, bodyScore and deterministicScore (0-100), isHighThreat, detectedLanguage, blacklistMatches (value, indicator, severity, threatType), whitelistedUrls, urlAnalysis (urlCount, indicators), redirects (originalUrl, finalUrl, hops), domainAges (domain, ageDays, isNewDomain), attachments (filename, mimeType, size, sha256, dangerousExtension, doubleExtension, macroEnabled, blacklisted) and senderHistory (null unless the tier uses it).

data.triageobject

How the verdict was reached. mode is off, shadow or live; in shadow, gates, reviews and floors are computed but change nothing. gate is llm, skip_malicious (critical threat-list match) or skip_legitimate (whitelisted or verified first-party sender), and gateApplied says whether it decided the result. identity, firstParty, bulk and auth summarize the evidence. escalation says whether a second, deeper pass was triggered and applied, with severity (second_opinion or must_review) and the first-pass and final verdicts. floorsApplied lists the identity floors that changed the result. Fields can be missing on results stored before they existed.

data.triage example

{
  "mode": "live",
  "gate": "llm",
  "gateApplied": false,
  "deterministicScore": 60,
  "identity": {
    "mismatch": true,
    "lures": ["payment"],
    "relayed": false,
    "lookalike": null,
    "brands": ["OLX"],
    "senderDomain": "so-net.ne.jp"
  },
  "firstParty": null,
  "bulk": { "isBulk": false, "network": null, "lure": true, "brand": null },
  "auth": { "spf": "pass", "dkim": "pass", "dmarc": "pass", "transportHeaders": true },
  "escalation": {
    "triggered": true,
    "applied": true,
    "severity": "must_review",
    "reasons": ["..."],
    "firstPassType": "legitimate",
    "firstPassScore": 12,
    "finalType": "phishing",
    "finalScore": 88
  },
  "floorsApplied": []
}
senderInsightsobject

What your own history says about the sender, computed after the verdict and never part of the score. status is ready (Pro and higher), locked (your plan does not include it), off (switched off in privacy settings) or unavailable (no readable sender). since is the oldest history behind it. Not stored with the analysis.

Each insight has a code and a severity (warning, caution, info, ok): first_time (domain, domainMessages), seen_before (messages, flagged, firstSeen), lookalike_domain (domain, displayDomain, knownDomain, knownMessages, technique: punycode, homoglyph, subdomain, typo, combo or tld), known_name_new_address (displayName, knownDomain, sameDomain) and reply_to_changed (replyToDomain, usedToBeNone).

Need help integrating the API? The support team replies within 24 hours on business days.