isMalicious Mail API
Inbound-message scan: what a mail's structure and the dataset say about it (gate scan meter, one unit per message)
Inbound-message scan: what a mail's structure and the dataset say about it (gate scan meter, one unit per message)
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/ismalicious-com:ismalicious-com-mail-api"
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Ismalicious Com Mail API
version: 1.2.0
contact:
name: isMalicious Support
url: https://ismalicious.com/support
email: contact@ismalicious.com
license:
name: Proprietary
url: https://ismalicious.com/terms
description: 'Operations tagged mail across 2 of this provider''s published API definitions: ismalicious-com-openapi.json, ismalicious-com-openapi2.json. Each path carries the servers of the definition it was published in.'
servers:
- url: https://api.ismalicious.com
description: Production server
security:
- ApiKeyAuth: []
- BearerAuth: []
- BasicAuth: []
tags:
- name: Mail
description: 'Inbound-message scan: what a mail''s structure and the dataset say about it (gate scan meter, one unit per message)'
paths:
/mail/scan:
post:
tags:
- Mail
summary: POST /mail/scan
operationId: scan
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/MailScanRequest'
required: true
responses:
'200':
description: Verdict, action, reasons and coverage
content:
application/json:
schema:
$ref: '#/components/schemas/MailScanResponse'
'400':
description: No message, several inputs, or input that is not an email
'401':
description: Unauthorized
'413':
description: The message exceeds 10 MiB
'429':
description: Scan quota exceeded
content:
application/json:
schema:
$ref: '#/components/schemas/QuotaBody'
'503':
description: 'The scan is busy: retry after the delay in Retry-After'
security:
- ApiKeyAuth: []
servers:
- url: https://api.ismalicious.com
description: Production server
components:
schemas:
MailCoverage:
type: object
required:
- ran
- skipped
- truncated
properties:
ran:
type: array
items:
type: string
skipped:
type: array
items:
$ref: '#/components/schemas/MailSkipped'
truncated:
type: boolean
description: A body, a header list, a link list or the attachment list hit a limit.
MailContext:
type: object
properties:
authservId:
type:
- string
- 'null'
description: 'The `authserv-id` your receiving system writes in its
`Authentication-Results` header. Only a header carrying it is read as
SPF/DKIM/DMARC evidence; anyone else can write one into a message.'
connectingIp:
type:
- string
- 'null'
description: 'The address that connected to your infrastructure, when you already
have it. Overrides the `Received` chain.'
trustAuthenticationResults:
type: boolean
description: 'Say that every `Authentication-Results` header in the message was
added by your own infrastructure (Exchange Online writes it without an
`authserv-id`). Needed, with or instead of `authservId`, for a message
to be `clean`.'
trustedHops:
type:
- integer
- 'null'
format: int32
description: '`Received` headers, counted from the top, that your own hosts wrote.
The connecting address is the one the last of them recorded. Default 1.'
minimum: 0
MailAuthentication:
type: object
required:
- status
properties:
dkim:
type:
- string
- 'null'
dmarc:
type:
- string
- 'null'
headerFrom:
type:
- string
- 'null'
spf:
type:
- string
- 'null'
status:
type: string
description: '`trusted` when the result below comes from a header you vouched for,
`unverified` when none did. This scan does not verify SPF, DKIM or
DMARC itself yet.'
MailScanResponse:
type: object
required:
- verdict
- recommendedAction
- riskScore
- headline
- reasons
- sender
- authentication
- links
- attachments
- contacts
- injection
- coverage
- latencyMs
properties:
attachments:
type: array
items:
$ref: '#/components/schemas/MailAttachment'
authentication:
$ref: '#/components/schemas/MailAuthentication'
connectingIp:
oneOf:
- type: 'null'
- $ref: '#/components/schemas/MailConnectingIp'
contacts:
type: array
items:
$ref: '#/components/schemas/MailContact'
description: Addresses and numbers written in the body.
coverage:
$ref: '#/components/schemas/MailCoverage'
description: What was checked and what was not.
headline:
type: string
description: One sentence a ticket or a chat message can carry.
injection:
$ref: '#/components/schemas/MailInjection'
latencyMs:
type: integer
format: int64
minimum: 0
links:
type: array
items:
$ref: '#/components/schemas/MailLink'
reasons:
type: array
items:
$ref: '#/components/schemas/MailReason'
description: The strongest findings first.
recommendedAction:
type: string
description: '`quarantine` | `review` | `warn` | `deliver`. `deliver` means no
objection from this scan; it never releases a message another engine
held.'
riskScore:
type: integer
format: int32
description: 0-100. 90 and above only for `malicious`.
minimum: 0
sender:
$ref: '#/components/schemas/MailSender'
verdict:
type: string
description: '`malicious` | `suspicious` | `clean` | `inconclusive`. `clean` is a
positive claim: it needs your own system''s DMARC pass, a sender domain
the dataset knows as established (among the 100 000 most visited, and
not a free mailbox) and every attachment and link vouched for or read.
A message with nothing against it and no such evidence is
`inconclusive`: a DMARC pass shows who sent a message, not that they
can be trusted.'
MailStructuredHeader:
type: object
required:
- name
- value
properties:
name:
type: string
value:
type: string
MailReason:
type: object
required:
- code
- severity
- summary
properties:
code:
type: string
description: 'Stable machine code: `link.listed`, `attachment.double_extension`…'
evidence:
type:
- string
- 'null'
description: What it is about; hosts are defanged (`evil[.]test`).
severity:
type: string
description: '`high` | `medium` | `low` | `info`.'
summary:
type: string
MailContact:
type: object
required:
- value
- kind
- verdict
- reasons
properties:
kind:
type: string
description: '`email` | `phone`.'
reasons:
type: array
items:
type: string
value:
type: string
verdict:
type: string
description: '`malicious` | `suspicious` | `unknown`.'
MailPosture:
type: object
description: 'The sender domain''s DNS-published mail posture, from the cached aggregate
([`super::posture`]).'
properties:
dmarcPolicy:
type:
- string
- 'null'
description: 'The DMARC policy that applies: `none` | `quarantine` | `reject`.'
grade:
type:
- string
- 'null'
description: '`A`..`F`, as the report''s DNS card grades it.'
hasDmarc:
type:
- boolean
- 'null'
description: 'A DMARC record applies to the sender domain: its own, or its
organizational domain''s.'
hasMx:
type:
- boolean
- 'null'
hasSpf:
type:
- boolean
- 'null'
spfAll:
type:
- string
- 'null'
description: 'The qualifier of the SPF record''s `all`: `fail` | `softfail` |
`neutral` | `pass`. Absent without an SPF record or without an `all`.'
spoofable:
type:
- boolean
- 'null'
description: 'Nothing the domain publishes would make a receiver that checks DMARC
reject a message forged with it: no DMARC record, a `p=none` record, or a
policy applied to under half of the failing mail. Absent when the DNS
answer cannot say.'
MailConnectingIp:
type: object
required:
- address
- verdict
- sources
- listed
properties:
address:
type: string
listed:
type: array
items:
type: string
sources:
type: integer
minimum: 0
verdict:
type: string
description: '`malicious` | `suspicious` | `clean` | `unknown`.'
MailSender:
type: object
required:
- verdict
- freeProvider
- disposable
- replyTo
properties:
address:
type:
- string
- 'null'
displayName:
type:
- string
- 'null'
disposable:
type: boolean
domain:
type:
- string
- 'null'
freeProvider:
type: boolean
mx:
type:
- boolean
- 'null'
description: The domain publishes MX hosts, from cached DNS; absent when not cached.
posture:
oneOf:
- type: 'null'
- $ref: '#/components/schemas/MailPosture'
description: The sender domain's published mail posture, from cached DNS.
replyTo:
type: array
items:
type: string
returnPath:
type:
- string
- 'null'
verdict:
type: string
description: 'What the dataset says about the address and its domain: `malicious` |
`suspicious` | `unknown`. Never `clean`: the dataset has no allowlist
for addresses.'
MailSkipped:
type: object
required:
- check
- reason
properties:
check:
type: string
reason:
type: string
MailLink:
type: object
required:
- url
- host
- verdict
- sources
- flags
properties:
anchorText:
type:
- string
- 'null'
flags:
type: array
items:
type: string
description: '`anchor_mismatch`, `ip_host`, `userinfo`, `punycode`, `mixed_script`,
`shortener`, `redirect_param`.'
host:
type: string
origin:
type:
- string
- 'null'
description: 'Where the link was found when it is not the message''s own text or HTML:
`attachment` (read out of a file''s bytes) or `attached_message`.'
originName:
type:
- string
- 'null'
description: The attachment or attached message the link was found in.
shownDomain:
type:
- string
- 'null'
description: The domain the visible text names, when it is not the target's.
sources:
type: integer
minimum: 0
url:
type: string
description: As written in the message. Defang it before relaying.
verdict:
type: string
description: '`malicious` | `suspicious` | `clean` | `unknown`.'
MailInjection:
type: object
required:
- score
- families
- hiddenScore
properties:
families:
type: array
items:
type: string
hiddenScore:
type: number
format: double
description: The same score over text a reader never sees.
score:
type: number
format: double
description: 'Prompt-injection heuristics over the whole body, as `/gate/scan` runs
them. 0.0..=1.0.'
MailStructuredAttachment:
type: object
properties:
contentType:
type:
- string
- 'null'
filename:
type:
- string
- 'null'
inline:
type: boolean
description: 'A part shown inside the body (`isInline` in Graph). One with a picture''s
type and name is listed, not looked up; any other inline part is read like
an attachment.'
md5:
type:
- string
- 'null'
sha1:
type:
- string
- 'null'
sha256:
type:
- string
- 'null'
description: Lowercase or uppercase hex. Any of the three is enough.
size:
type:
- integer
- 'null'
format: int64
minimum: 0
MailAttachment:
type: object
required:
- inline
- digests
- verdict
- knownGood
- flags
properties:
contentType:
type:
- string
- 'null'
detectedType:
type:
- string
- 'null'
description: 'What the bytes are, for a message sent raw: `pe`, `elf`, `macho`, `lnk`,
`zip`, `ooxml`, `jar`, `ole`, `pdf`, `rtf`, `html`, `text`, `image`,
`gzip`, `rar`, `7z`, `cab`, `iso` or `unknown`. Absent for a message
that came as metadata and digests.'
digests:
$ref: '#/components/schemas/MailDigests'
family:
type:
- string
- 'null'
filename:
type:
- string
- 'null'
flags:
type: array
items:
type: string
description: 'From the name and the declared type: `risky_extension`,
`double_extension`, `bidi_override`, `type_mismatch`, `archive`. From the
bytes, for a message sent raw: `disguised_program`, `macro_project`,
`encrypted_archive`, `archive_risky`, `remote_template`,
`embedded_object`, `pdf_launch`, `pdf_script`, `html_smuggling`,
`password_form`, `svg_script`, `command_line`. `attached_message` marks a
`message/rfc822` attachment, read as a message of its own.'
inline:
type: boolean
description: 'Shown inside the body: listed, not looked up.'
knownGood:
type: boolean
description: NSRL knows the file as known software.
origin:
type:
- string
- 'null'
description: '`attached_message` when the file came out of a `message/rfc822`
attachment; absent for the message''s own attachments.'
originName:
type:
- string
- 'null'
description: The attached message the file came out of.
size:
type:
- integer
- 'null'
format: int64
minimum: 0
verdict:
type: string
description: 'What the hash tier says: `malicious` | `suspicious` | `clean` |
`unknown`. `unknown` is not a safety claim.'
MailStructuredMessage:
type: object
description: A message already parsed by the caller.
properties:
attachments:
type: array
items:
$ref: '#/components/schemas/MailStructuredAttachment'
description: Attachments as metadata and digests. Contents are never needed.
from:
type:
- string
- 'null'
description: '`From`, when `headers` does not carry it: `Name <address>` or an address.'
headers:
type: array
items:
$ref: '#/components/schemas/MailStructuredHeader'
description: 'Every header, in message order, as received (`internetMessageHeaders`
in Microsoft Graph). Needed for `Received` and `Authentication-Results`.'
html:
type:
- string
- 'null'
description: The HTML body.
replyTo:
type:
- string
- 'null'
returnPath:
type:
- string
- 'null'
subject:
type:
- string
- 'null'
text:
type:
- string
- 'null'
description: The plain-text body.
MailDigests:
type: object
properties:
md5:
type:
- string
- 'null'
sha1:
type:
- string
- 'null'
sha256:
type:
- string
- 'null'
MailScanRequest:
type: object
properties:
context:
$ref: '#/components/schemas/MailContext'
description: What you know about your own infrastructure.
eml:
type:
- string
- 'null'
description: 'The whole message as UTF-8 text. Use `emlBase64` for 8-bit or binary
parts.'
emlBase64:
type:
- string
- 'null'
description: 'The whole message, RFC 5322 (`.eml`), base64. Up to 10 MiB decoded.
Send exactly one of `emlBase64`, `eml` and `message`.'
message:
oneOf:
- type: 'null'
- $ref: '#/components/schemas/MailStructuredMessage'
description: 'A message already parsed by the caller (Microsoft Graph, Gmail API):
headers, bodies and attachment digests.'
QuotaBody:
type: object
description: 'The scan-quota 429 (`/gate/scan`, `/gate/url`, `/mail/scan`).
`error`, `usage` (a number, unlike the request 429''s object) and `limit`
are the original contract and stay as they were; every other field was
added on 2026-10-04 so the refusal says when it lifts and what the caller
can do, as the request-quota 429 does.'
required:
- error
- usage
- limit
properties:
billingIssue:
type:
- boolean
- 'null'
description: 'True when the caller''s paid subscription is suspended (payment past
due, unpaid, incomplete or paused): settle it, buy nothing.'
cta:
type: object
description: '`primary` (`upgrade`, `contact` or `billing`), `url`, `reason`. A Free
caller''s points at Pro.'
docs:
type:
- string
- 'null'
error:
type: string
limit:
type: integer
format: int64
description: Scans included per month on the caller's plan.
minimum: 0
message:
type:
- string
- 'null'
description: The refusal in words, with the day the allowance resets.
promoCode:
type:
- string
- 'null'
description: First-transaction promo, offered to a Free caller only.
promoDiscount:
type:
- integer
- 'null'
format: int32
minimum: 0
resetsAt:
type:
- string
- 'null'
description: 'When the allowance resets: 00:00 UTC on the 1st of next month.'
example: '2026-11-01T00:00:00Z'
retryAfter:
type:
- integer
- 'null'
format: int64
description: 'Seconds until the reset. Deliberately not sent as a `Retry-After`
header: a client that sleeps on it before retrying would wait days.'
upgradeRequired:
type:
- boolean
- 'null'
description: True when buying a plan lifts the limit (Free, Basic).
usage:
type: integer
format: int64
description: Scans used this month.
minimum: 0
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: X-API-KEY
description: Base64 encoded `apiKey:apiSecret`. Get your keys from https://ismalicious.com/app/account
BasicAuth:
type: http
scheme: basic
description: 'Username = apiKey, password = apiSecret. Legacy: any username, password = Base64(apiKey:apiSecret).'
BearerAuth:
type: http
scheme: bearer
description: '`Authorization: Bearer <token>` where token = Base64(apiKey:apiSecret) — the same credential as X-API-KEY.'
sessionCookie:
type: apiKey
in: cookie
name: __Secure-authjs.session-token
description: Authenticated browser session. The claim endpoint also requires a verified email in the database; organization API keys cannot claim watches.
x-refined-from:
- ismalicious-com-openapi.json
- ismalicious-com-openapi2.json