Phishing triage is high-volume, judgment-heavy, and unforgiving in both directions: call a real phish benign and a user gets compromised; call a legitimate email malicious and you burn analyst time and user trust.
AI is a strong first-pass analyst here — it can read headers, URLs, and body text, correlate against threat-intel indicators, and produce a verdict with reasoning far faster than manual review. But a verdict is only useful if it's defensible, and both error directions are costly, so the discipline is to confirm the indicators the model reasoned over are real and to independently check the verdict before it drives an action like blocking a sender or releasing a quarantined message.
A phishing verdict has two failure modes, and the "benign" verdict is the one that compromises someone. Verify a clean call as rigorously as a malicious one — attackers craft emails specifically to read as legitimate.
Collect the artifact and its context — full headers, the raw body, extracted URLs and attachments' hashes (never detonate live). Pull indicator reputation from your threat-intel sources.
Prompt for a structured verdict with reasoning, driven through the SDK for consistency:
import anthropic
from pydantic import BaseModel
class Verdict(BaseModel):
verdict: str # "Malicious" | "Suspicious" | "Benign"
confidence: str # "High" | "Medium" | "Low"
indicators: list[str] # the specific signals that drove it
rationale: str
recommended_action: str
client = anthropic.Anthropic()
response = client.messages.parse(
model="claude-opus-4-8",
max_tokens=4000,
thinking={"type": "adaptive"},
messages=[{
"role": "user",
"content": (
"Analyze this email for phishing. Base the verdict on concrete "
"signals: header/SPF-DKIM-DMARC results, sender vs reply-to "
"mismatch, URL/display-text mismatch, lookalike domains, "
"urgency/credential-harvesting language, and the reputation data "
"provided. List the exact indicators. If signals conflict, say "
"Suspicious rather than guessing.\n\n" + email_and_intel
),
}],
output_format=Verdict,
)
v = response.parsed_output
print(v.verdict, v.confidence, v.indicators)
Expected output: a verdict tied to named indicators (e.g., "DMARC fail; reply-to on a lookalike domain registered 3 days ago; URL text says the bank, href goes elsewhere") with a confidence that drops to Suspicious when signals conflict.
Extract and enrich indicators for the wider picture. Have the model pull all IOCs, then check each against your TIP / reputation services — a sender that's benign in isolation may be part of a tracked campaign.
Cross-check the verdict against independent signals. Confirm the SPF/DKIM/DMARC results the model cited by reading the raw headers yourself, and confirm URL reputation against a service rather than the model's assertion.
Act, then feed back. Block, quarantine, or release based on the confirmed verdict, and turn confirmed-malicious indicators into detections (see the detection-authoring workflows) so the next instance is caught automatically.
Phishing artifacts contain the client's real user addresses, internal hostnames, and sometimes credentials the phish already harvested. Before prompting, redact recipient PII and internal identifiers, keeping the security-relevant structure — headers, URL patterns, body tells. Where the client bars external AI, run a local model for the analysis; indicator enrichment can use the client's own MISP/OpenCTI instance. Never submit a client's actual email or attachment to a public multi-scanner without approval (it may expose their data), never detonate a live sample outside an approved sandbox, and confirm per-client tool approval before the first prompt.