isMalicious Mail API

Inbound-message scan: what a mail's structure and the dataset say about it (gate scan meter, one unit per message)

GitHub OpenAPI

Operations 1

POST /mail/scan POST /mail/scan #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • 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.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/ismalicious-com:ismalicious-com-mail-api"
All apis
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.

Get an API 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 Specification

ismalicious-com-mail-api-openapi.yml Raw ↑
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