Inbound Email
Receive and classify inbound email with intent detection, routing actions, and safety flags.
Molted Email can receive inbound email on your mailboxes, classify intent, and route messages to the right handler.
List inbound messages
GET https://api.molted.email/v1/inboundcurl "https://api.molted.email/v1/inbound?tenantId=tenant_abc123" \
-H "Authorization: Bearer YOUR_API_KEY"Response
[
{
"id": "msg_abc123",
"tenantId": "tenant_abc123",
"fromEmail": "customer@example.com",
"toEmail": "support@yourdomain.com",
"subject": "Need help with billing",
"bodyText": "I have a question about my invoice...",
"classification": {
"intent": "billing",
"confidence": 0.92,
"suggestedAction": "notify_owner"
},
"createdAt": "2026-03-01T12:00:00Z"
}
]Approve routing
For messages that require manual approval before being routed:
POST https://api.molted.email/v1/inbound/:id/approvecurl -X POST https://api.molted.email/v1/inbound/msg_abc123/approve \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tenantId": "tenant_abc123",
"action": "notify_owner"
}'Classify intent
Classify the intent of an inbound email to determine how your agent should respond.
molted classify --subject "Re: Invoice" --body "Where is my invoice?"curl -X POST "https://api.molted.email/v1/agent/classify-intent" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tenantId": "tenant_abc123",
"subject": "Re: Invoice",
"bodyText": "Where is my invoice?"
}'Classify response
{
"intent": "billing",
"confidence": 0.92,
"suggestedAction": "notify_owner",
"classifierVersion": "v3-safety",
"flags": [],
"allScores": [
{ "intent": "billing", "score": 0.92, "keywordMatches": 3, "subjectMatches": 1, "bodyMatches": 2 },
{ "intent": "support", "score": 0.15, "keywordMatches": 1, "subjectMatches": 0, "bodyMatches": 1 },
{ "intent": "interested", "score": 0, "keywordMatches": 0, "subjectMatches": 0, "bodyMatches": 0 },
{ "intent": "not_now", "score": 0, "keywordMatches": 0, "subjectMatches": 0, "bodyMatches": 0 },
{ "intent": "objection", "score": 0, "keywordMatches": 0, "subjectMatches": 0, "bodyMatches": 0 },
{ "intent": "legal", "score": 0, "keywordMatches": 0, "subjectMatches": 0, "bodyMatches": 0 },
{ "intent": "security", "score": 0, "keywordMatches": 0, "subjectMatches": 0, "bodyMatches": 0 },
{ "intent": "out_of_office", "score": 0, "keywordMatches": 0, "subjectMatches": 0, "bodyMatches": 0 }
],
"runnerUpIntent": "support",
"runnerUpConfidence": 0.15,
"safetyVerdict": "clean",
"safetyAction": "deliver"
}| Field | Description |
|---|---|
intent | The top-scoring intent category. This is the primary signal for your agent logic. |
confidence | Score between 0 and 1 for the top intent. |
suggestedAction | Recommended routing action (see Routing actions). |
safetyVerdict | Safety classification result (see Safety classification). |
safetyAction | Action the safety system recommends: deliver, quarantine, or reject. |
flags | Array of safety flags that may require attention (see Safety flags). |
runnerUpIntent | Second-highest scoring intent. Present when a runner-up scores above 0. |
runnerUpConfidence | Score of the runner-up intent. |
Understanding allScores
The allScores array contains scores for all 8 intent categories, sorted by score descending. Each entry includes:
| Field | Description |
|---|---|
intent | The intent category name. |
score | Classification score (0-1). Higher means stronger signal. |
keywordMatches | Total keyword hits across subject and body. |
subjectMatches | Keyword hits in the subject line only. |
bodyMatches | Keyword hits in the body only. |
In most cases, you only need the top-level intent and confidence fields. The allScores array is useful when:
- Competing intents: two or more intents have non-zero scores, and you want to understand ambiguity
- Debugging: you want to see why a message was classified a certain way
- Custom routing: you apply your own thresholds instead of using the default
suggestedAction
Entries with score: 0 had no keyword matches and can be ignored.
Batch classify
Classify multiple messages in a single request:
molted classify batch --emails '[
{"subject": "Re: Order", "body": "When does it ship?"},
{"subject": "Thanks!", "body": "Got it, looks great."}
]'curl -X POST "https://api.molted.email/v1/agent/batch/classify-intent" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tenantId": "tenant_abc123",
"messages": [
{"subject": "Re: Order", "bodyText": "When does it ship?"},
{"subject": "Thanks!", "bodyText": "Got it, looks great."}
]
}'The response contains a results array with one classification per message, in the same order as the input.
Intent categories
When an inbound message is classified, it is assigned one of these intent categories:
| Intent | Description |
|---|---|
interested | Recipient expressed interest or wants to move forward. |
not_now | Recipient is not interested at this time but may be later. |
objection | Recipient raised a concern or objection. |
support | Recipient needs help or has a support question. |
billing | Recipient has a billing-related question or issue. |
legal | Message relates to legal matters. |
security | Message relates to a security concern. |
out_of_office | Automated out-of-office reply. |
unclassified | Could not determine a clear intent. |
Each classification also includes a confidence score between 0 and 1, and a suggestedAction.
Routing actions
The classification engine suggests one of these routing actions:
| Action | Description |
|---|---|
notify_owner | Notify the contact's assigned owner. |
require_approval | Hold the message until manually approved. |
auto_archive | Archive the message automatically (e.g., out-of-office replies). |
escalate | Escalate to a human reviewer for handling. |
spam | Route to the spam queue for review. |
Safety classification
Inbound messages are run through a safety classifier that produces a verdict:
| Verdict | Description |
|---|---|
clean | No threats detected. |
spam | Unsolicited bulk email. Routed to the dedicated spam queue. |
phishing | Credential harvesting attempt. Routed to approval queue for human review. |
malware | Dangerous content detected. Routed to approval queue. |
abuse | Threats or harassment. Routed to approval queue. |
impersonation | Spoofing or BEC attempt. Routed to approval queue. |
Only spam-verdict messages go to the spam queue. All other safety-flagged messages (phishing, malware, abuse, impersonation) require human review in the approval queue.
Spam queue
Messages classified as spam are routed to a dedicated spam queue, separate from the approval queue.
List spam messages
GET https://api.molted.email/v1/override/queues/spamMark as not spam
Release a message from spam and move it to the inbox:
POST https://api.molted.email/v1/override/:threadId/not-spam{
"reason": "This is a legitimate message"
}Delete spam
Permanently remove a spam message:
POST https://api.molted.email/v1/override/:threadId/delete-spamQueue counts
The queue counts endpoint now includes a spam count:
GET https://api.molted.email/v1/override/queues/counts{
"needs_approval_outbound": 3,
"needs_approval_inbound": 1,
"blocked_by_policy": 0,
"high_risk": 0,
"spam": 5
}Report spam
Move an inbox thread to the spam queue and record feedback:
POST https://api.molted.email/v1/override/:threadId/report-spamThis action moves the thread into the spam queue and records spam feedback that improves future classification accuracy for that sender.
Spam feedback
Every time a message is marked as spam, not spam, or deleted from spam, a feedback record is created. This feedback trains the classifier to improve accuracy over time for each tenant.
Feedback effects:
- 3+ spam reports from the same sender email: strong spam signal (+0.5) for future messages
- 3+ spam reports from the same domain: moderate spam signal (+0.4)
- 3+ not-spam marks for the same sender: auto-whitelists the sender
Feedback stats
View a summary of all spam feedback for your tenant:
GET https://api.molted.email/v1/override/spam-feedback/stats{
"total": 42,
"spamReports": 30,
"notSpamReports": 12,
"uniqueDomains": 8
}Sender reputation
Molted tracks reputation for inbound sender domains. The reputation score (0-1) is computed from:
- Positive signals: replies sent to the sender, passing SPF/DKIM/DMARC, not-spam feedback
- Negative signals: spam classifications, spam feedback, auth failures
- Decay: scores drift toward 0.5 (neutral) with a 30-day half-life when no activity is seen
The classifier uses reputation to adjust spam scores:
- Score below 0.2 (known spammer): adds +0.3 to spam score
- Score above 0.8 (trusted sender): reduces spam score by 0.2
Query sender reputation
GET https://api.molted.email/v1/override/sender-reputation?domain=example.com[
{
"domain": "example.com",
"email": null,
"totalReceived": 150,
"spamCount": 2,
"notSpamCount": 5,
"repliedCount": 12,
"authPassCount": 145,
"authFailCount": 3,
"reputationScore": 0.82,
"lastSeenAt": "2026-03-25T10:00:00Z"
}
]Safety settings
Safety settings control tenant-level guardrails for inbound message handling. They include per-threat actions, automation flags, and spam filter rules. Settings are tenant-wide and affect all agents and API keys.
Get safety settings
molted safety getcurl "https://api.molted.email/v1/agent/config/safety-settings" \
-H "Authorization: Bearer YOUR_API_KEY"{
"tenantId": "tenant_abc123",
"quarantineHighInjection": true,
"holdCriticalAnomalies": true,
"blockCanaryViolations": true,
"spamAction": "quarantine",
"phishingAction": "quarantine",
"malwareAction": "reject",
"abuseAction": "quarantine",
"impersonationAction": "quarantine",
"spamThreshold": 0.5,
"maxLinksThreshold": 5,
"blockNoAuth": false,
"blockedKeywords": [],
"allowedSenders": [],
"spamActionLowConfidence": "deliver"
}Update safety settings
molted safety update --settings '{
"spamThreshold": 0.3,
"blockNoAuth": true,
"malwareAction": "reject"
}'curl -X PUT "https://api.molted.email/v1/agent/config/safety-settings" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"spamThreshold": 0.3,
"blockNoAuth": true,
"malwareAction": "reject"
}'All fields are optional -- only include what you want to change. Unspecified fields retain their current values.
Safety automation flags
| Field | Type | Default | Description |
|---|---|---|---|
quarantineHighInjection | boolean | true | Quarantine messages flagged with injection_risk. |
holdCriticalAnomalies | boolean | true | Hold messages with critical safety anomalies for human review. |
blockCanaryViolations | boolean | true | Block messages that trigger canary token violations. |
Per-threat actions
Each threat type can be set to deliver, quarantine, or reject.
| Field | Type | Default | Description |
|---|---|---|---|
spamAction | string | quarantine | Action for messages classified as spam. |
phishingAction | string | quarantine | Action for phishing attempts. |
malwareAction | string | reject | Action for dangerous content. |
abuseAction | string | quarantine | Action for threats or harassment. |
impersonationAction | string | quarantine | Action for spoofing or BEC attempts. |
Spam filter rules
| Field | Type | Default | Description |
|---|---|---|---|
spamThreshold | number | 0.5 | Spam verdict threshold (0.1-1.0). Lower means more aggressive filtering. |
maxLinksThreshold | integer | 5 | Max links before triggering the excessive_links signal (1-100). |
blockNoAuth | boolean | false | When true, emails failing all auth checks (SPF+DKIM+DMARC) receive a heavy spam penalty (+0.5). |
blockedKeywords | string[] | [] | Custom keyword blocklist (max 100 entries). Each match adds +0.4 to the spam score. |
allowedSenders | string[] | [] | Email addresses or domains that bypass spam classification entirely (max 100 entries). |
spamActionLowConfidence | string | deliver | Action for spam between the custom threshold and 0.5 confidence: deliver, quarantine, or reject. |
{
"spamThreshold": 0.3,
"maxLinksThreshold": 3,
"blockNoAuth": true,
"blockedKeywords": ["crypto", "investment opportunity", "wire transfer"],
"allowedSenders": ["trusted-partner.com", "internal@mycompany.com"],
"spamActionLowConfidence": "quarantine"
}Rule evaluation order
- Allowed senders and the sender whitelist are checked first -- matching senders bypass all classification.
- Blocked keywords are matched case-insensitively against the combined subject + body.
- Block unauthenticated checks SPF, DKIM, and DMARC results.
- Max links threshold counts links in the body text and HTML.
- All signals are aggregated and compared against the spam threshold.
- Low-confidence spam (between threshold and 0.5) uses the low-confidence action.
Safety flags
Messages may be flagged with safety indicators that require additional attention:
| Flag | Description |
|---|---|
conflicting_intents | Multiple intent signals detected with similar scores - may need human review. |
low_confidence | Classification confidence is below 0.6 - routing may be unreliable. |
injection_risk | Prompt injection patterns detected in the message. |
adversarial_position | Subject implies one intent but body matches a different, action-oriented intent. |
thread_anomaly | Message behavior deviates from the thread's established pattern. |