Skip to main content

Squillify development is paused indefinitely. Read why, and discover what this experiment achieved. Read the story

MicroSkills Reference

Email Message Parser

communication.email_parse

ProductionBounded custom (Class B)Deterministicstrictfastv1.0.0
No network

Overview

Nothing here is sent, fetched or connected to: you hand us the raw text of one message and we hand back its structure. We read the headers in the order they arrived and keep every duplicate, because a Received chain and a second From header are usually the reason somebody is looking at a message in the first place. We decode the encoded words in a subject, unfold folded headers, walk the MIME tree, and pull out the best text or HTML alternative up to the size you ask for. Attachments come back as metadata only — name, type, size, optional checksum — and never as content: the bytes may be enormous or hostile, and you already have the message they came from. When something in the message cannot be determined, we say so in warnings and mark the answer partial rather than guessing: an unknown charset, a broken base64 run or a boundary that stops half way is described, never repaired, and the fallback we applied is named. Nothing about a remote reference is followed, so a message pointing at a tracking pixel, an external entity or a remote DTD is read as inert text.

Real mail is nothing like the examples. Headers fold across lines, subjects arrive as =?utf-8?B?...?=, the same header appears four times, bodies are base64 in a charset nobody declared, and a filename contains a right-to-left override that makes an executable look like a PDF. Writing that parser yourself means owning a security-sensitive corpus of malformed input forever. We read it under published bounds, refuse what crosses them before expanding anything, and tell you plainly what we could not work out.

Reliability

The same bytes always produce the same answer: there is no clock, no randomness, no cache and nothing outside your request, and a Date that cannot be read is left without an instant rather than filled in from the current time. Three things are worth knowing before you rely on the result. First, filename is written by whoever sent the message: it may contain ../, an absolute path, a NUL, a CR/LF pair, or a right-to-left override that disguises the extension. Never build a path from it — filenameSafe exists for that, reduced to one segment with the invisible characters dropped. Second, redactAddresses keeps the domain and drops the local part in the address list and in the From, To, Cc, Bcc, Reply-To, Sender, Return-Path and Delivered-To headers; it does not touch the body, a signature or a subject, so a message quoting an address in its text still carries it, and a domain beside a subject line can still identify one person. Third, a partial answer is normal for real mail and is not an error. Eight things stop the call instead of answering it. EMAIL_INVALID means the bytes carry no recognisable header at all, so they were never a message — check you sent one message rather than a mailbox file, and expect the same refusal if you retry unchanged. EMAIL_TOO_LARGE means the message is past 2000000 bytes once encoded, checked before parsing starts, so nothing was read. EMAIL_HEADER_LIMIT_EXCEEDED and EMAIL_HEADER_TOO_LONG mean a part declares more than 256 headers or one header is longer than 8192 characters. EMAIL_PART_LIMIT_EXCEEDED and EMAIL_DEPTH_EXCEEDED mean the message has more than 128 parts or nests deeper than 8 levels; both are refused while the message is still bytes, before anything is expanded, which is what keeps a message engineered to be enormous from costing anything. EMAIL_ATTACHMENT_LIMIT_EXCEEDED means more attachments than the limit, and the list is refused rather than truncated into something that looks complete. EMAIL_OUTPUT_TOO_LARGE means the description itself would be too big; ask for less of the message with includeBody NONE or a smaller maxBodyChars. None of these is worth retrying unchanged: each names the limit and what was observed, so lower the matching option or send a smaller message.

Context footprint

The answer is as large as you ask for. With includeBody NONE it is headers, structure and attachment metadata — normally a few kilobytes. The body is bounded by maxBodyChars, 262144 by default, and is cut and flagged rather than allowed to grow. Attachment content is never returned at any size, and nothing — no address, subject, body or filename — is written to a log. A successful call costs 1 execution credit.

When to use it

Whenever you hold the raw text of a message and want its structure: headers, addresses, thread references, body text, or an inventory of what is attached.

When you want mail sent or fetched, when you need to know whether an address is real rather than well-formed, or when you want the content of an attachment rather than a description of it.

Scope

Note
Reads one raw RFC 5322 message into its headers, addresses, body text and attachment metadata, and says what it could not determine.

Non-goals

  • Does not send, receive, or open an SMTP, IMAP or gateway connection
  • Does not resolve DNS, check an MX record, or tell you whether an address receives mail
  • Does not follow a remote DTD, external entity, stylesheet or image the message references
  • Does not return attachment content — file.detect_mime, file.hash and file.safe_extract_archive are the skills for handling bytes
  • Does not render or execute anything found in the body
  • Does not repair a broken message, invent a boundary, or guess a charset silently
  • Does not check SPF, DKIM or DMARC, which need DNS and cryptography

Properties

IdempotentRetry safeStreaming supportedFile input supportedRequires external service

Capabilities

parsing

Cost & billing

Flat rate per invocation

MetercallsBillable onsuccessCost per call1 credit

File handling

Accepts file_refProduces file_refMax input2 MB

How we handle your data

Sensitive output possible
What this means

This skill may receive sensitive content and may return submitted, transformed, extracted, or reformatted content.

Squillify guarantees
  • Runs inside Squillify
  • This skill runs inside Squillify.
  • Payloads are not sent to an external service by this skill.
  • Squillify does not permanently store submitted payloads.
  • Telemetry records metadata only, not submitted content.
  • Submitted payloads are not used for training.
  • Submitted payloads are not reviewed by humans.
  • This skill does not target sensitive data.
  • The response may contain submitted or transformed content.
Your responsibility

Squillify does not determine whether submitted content is sensitive. Your application is responsible for deciding what data it is allowed to submit. Your application is responsible for securing the response.

Read the Microskill Data Handling Policy

Input schema

FieldTypeRequiredDescription
rawEmailstringYesRaw RFC message parsed without fetching external content.
includeBodystringNoBody projection; omit it for TEXT.
maxBodyCharsintegerNoPer-body character cap; omit it for 100,000.
decodeEncodedWordsbooleanNoWhether encoded header words are decoded; omit it for true.
redactAddressesbooleanNoWhether mailbox addresses are redacted; omit it for false.
includeAttachmentChecksumsbooleanNoWhether decoded attachment checksums are calculated; omit it for false.
maxPartsintegerNoMIME part cap; omit it for one thousand.
maxDepthintegerNoMIME nesting-depth cap; omit it for twenty.
maxAttachmentsintegerNoAttachment cap; omit it for one hundred.

Output schema

FieldTypeRequiredDescription
headersarrayYesBounded decoded headers in source order. The item shape is defined by the nested schema and the array contains from 0 through 10000 items.
headerIndexobjectYesPurpose-bound case-normalized header index. Its documented communication properties are bounded to 256 entries — unconstrained RFC 8259 JSON items.
addressesobjectYesClosed from, to, cc, bcc, reply-to, and sender address receipt. Its documented communication properties are bounded to 256 entries — unconstrained RFC 8259 JSON items.
subjectstringNoDecoded message subject when present.
messageIdstringNoMessage identifier when present.
dateobjectNoClosed parsed date receipt when present. Its documented communication properties are bounded to 256 entries — unconstrained RFC 8259 JSON items.
inReplyTostringNoIn-Reply-To value when present.
referencesarrayNoBounded message reference identifiers when present. The item shape is defined by the nested schema and the array contains from 0 through 10000 items.
bodyobjectYesClosed bounded text and HTML body receipt. Its documented communication properties are bounded to 256 entries — unconstrained RFC 8259 JSON items.
attachmentsarrayYesBounded attachment metadata receipts. The item shape is defined by the nested schema and the array contains from 0 through 1000 items.
structureobjectYesClosed MIME structure and truncation receipt. Its documented communication properties are bounded to 256 entries — unconstrained RFC 8259 JSON items.
warningsarrayYesBounded array of non-fatal warning strings. The item shape is defined by the nested schema and the array contains from 0 through 10000 items.
parsedstringYesStable parse-status label.
summaryobjectYesClosed message count and size summary. Its documented communication properties are bounded to 256 entries — unconstrained RFC 8259 JSON items.

API contract

POST /v1/execute
curl
curl -sS -X POST "$BASE/v1/execute" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{"skillKey":"communication.email_parse","input":{"rawEmail":"From: [email protected]\r\nSubject: Hello\r\n\r\nBody.","includeBody":"TEXT"}}'
Integrations
TypeScript SDK
import { Squillify } from "@squillify/sdk";

const sq = new Squillify({ apiKey: process.env.SQUILLIFY_API_KEY });

const { output } = await sq.execute("communication.email_parse", {"rawEmail":"Received: from mail.northwind.example by mx.acme.example\r\nDate: Tue, 28 Jul 2026 09:14:02 +0200\r\nFrom: Ada Lovelace <[email protected]>\r\nTo: Support <[email protected]>\r\nMessage-ID: <[email protected]>\r\nSubject: =?utf-8?Q?Order_4417_=E2=80=94_wrong_address?=\r\nContent-Type: text/plain; charset=utf-8\r\n\r\nThe delivery address on order 4417 is out of date. Please use 14 Rue Lafayette instead."});

Errors

CodeHTTPRetryableDescription
VALIDATION_ERROR400NoThe request payload failed schema validation.
UNAUTHORIZED401NoMissing or invalid API key.
SKILL_NOT_ACCESSIBLE403NoThe API key is not granted access to this skill.
SKILL_NOT_AVAILABLE_FOR_PLAN403NoThis skill is not included in your current plan.
PLAN_ELIGIBILITY_UNAVAILABLE503YesPlan eligibility could not be decided. Retry after backoff.
SKILL_NOT_FOUND404NoThe requested skill or version does not exist.
TENANT_RATE_LIMITED429YesRequest rate limit exceeded. Retry after backoff.
QUOTA_EXCEEDED429YesPlan quota exhausted. Retryable once the quota period resets.
EXECUTION_TIMEOUT504YesExecution exceeded the allowed time budget.
EMAIL_INVALID400NoThe request payload failed this skill's input validation.
EMAIL_TOO_LARGE429NoA size, quota, rate, or time limit was exceeded.
EMAIL_HEADER_LIMIT_EXCEEDED429NoA size, quota, rate, or time limit was exceeded.
EMAIL_HEADER_TOO_LONG429NoA size, quota, rate, or time limit was exceeded.
EMAIL_PART_LIMIT_EXCEEDED429NoA size, quota, rate, or time limit was exceeded.
EMAIL_DEPTH_EXCEEDED429NoA size, quota, rate, or time limit was exceeded.
EMAIL_ATTACHMENT_LIMIT_EXCEEDED429NoA size, quota, rate, or time limit was exceeded.
EMAIL_OUTPUT_TOO_LARGE429NoA size, quota, rate, or time limit was exceeded.

Other limits

LimitValue
Recommended inline bytes32 KiB

Limitations

  • Does not send, receive, or open an SMTP, IMAP or gateway connection
  • Does not resolve DNS, check an MX record, or tell you whether an address receives mail
  • Does not follow a remote DTD, external entity, stylesheet or image the message references
  • Does not return attachment content — file.detect_mime, file.hash and file.safe_extract_archive are the skills for handling bytes
  • Does not render or execute anything found in the body
  • Does not repair a broken message, invent a boundary, or guess a charset silently
  • Does not check SPF, DKIM or DMARC, which need DNS and cryptography
  • Sending a reply, which needs a transport we deliberately do not have
  • Deciding whether a message is spam or whether its sender is authentic
  • Getting the bytes of an attachment out of a message
  • Splitting a mailbox file, an mbox or a partial IMAP fetch into messages

How to call

POST /v1/execute (requires authentication)

Request bodyjson
{
  "skillKey": "communication.email_parse",
  "input": {
    "rawEmail": "Received: from mail.northwind.example by mx.acme.example\r\nDate: Tue, 28 Jul 2026 09:14:02 +0200\r\nFrom: Ada Lovelace <[email protected]>\r\nTo: Support <[email protected]>\r\nMessage-ID: <[email protected]>\r\nSubject: =?utf-8?Q?Order_4417_=E2=80=94_wrong_address?=\r\nContent-Type: text/plain; charset=utf-8\r\n\r\nThe delivery address on order 4417 is out of date. Please use 14 Rue Lafayette instead."
  }
}

Examples

A plain support reply, with an encoded subject and a Received line

Inputjson
{
  "rawEmail": "Received: from mail.northwind.example by mx.acme.example\r\nDate: Tue, 28 Jul 2026 09:14:02 +0200\r\nFrom: Ada Lovelace <[email protected]>\r\nTo: Support <[email protected]>\r\nMessage-ID: <[email protected]>\r\nSubject: =?utf-8?Q?Order_4417_=E2=80=94_wrong_address?=\r\nContent-Type: text/plain; charset=utf-8\r\n\r\nThe delivery address on order 4417 is out of date. Please use 14 Rue Lafayette instead."
}
Expected outputjson
{
  "headers": [
    {
      "name": "Received",
      "value": "from mail.northwind.example by mx.acme.example"
    },
    {
      "name": "Date",
      "value": "Tue, 28 Jul 2026 09:14:02 +0200"
    },
    {
      "name": "From",
      "value": "Ada Lovelace <[email protected]>"
    },
    {
      "name": "To",
      "value": "Support <[email protected]>"
    },
    {
      "name": "Message-ID",
      "value": "<[email protected]>"
    },
    {
      "name": "Subject",
      "decodedValue": "Order 4417 — wrong address",
      "value": "=?utf-8?Q?Order_4417_=E2=80=94_wrong_address?="
    },
    {
      "name": "Content-Type",
      "value": "text/plain; charset=utf-8"
    }
  ],
  "warnings": [],
  "attachments": [],
  "parsed": "complete",
  "addresses": {
    "from": [
      {
        "address": "[email protected]",
        "domain": "northwind.example",
        "valid": true,
        "name": "Ada Lovelace"
      }
    ],
    "to": [
      {
        "address": "[email protected]",
        "domain": "acme.example",
        "valid": true,
        "name": "Support"
      }
    ],
    "cc": [],
    "bcc": [],
    "replyTo": []
  },
  "structure": {
    "contentType": "text/plain",
    "parts": []
  },
  "headerIndex": {
    "date": "Tue, 28 Jul 2026 09:14:02 +0200",
    "from": "Ada Lovelace <[email protected]>",
    "to": "Support <[email protected]>",
    "content-type": "text/plain; charset=utf-8",
    "subject": "Order 4417 — wrong address",
    "message-id": "<[email protected]>"
  },
  "summary": {
    "sizeBytes": 407,
    "partCount": 1,
    "attachmentCount": 0,
    "redacted": false,
    "headerCount": 7,
    "depth": 1,
    "hasHtml": false,
    "hasText": true
  },
  "messageId": "<[email protected]>",
  "subject": "Order 4417 — wrong address",
  "date": {
    "raw": "Tue, 28 Jul 2026 09:14:02 +0200",
    "instant": "2026-07-28T07:14:02Z"
  },
  "body": {
    "text": "The delivery address on order 4417 is out of date. Please use 14 Rue Lafayette instead.",
    "truncated": false,
    "charset": "UTF-8"
  }
}
Tip
The subject arrived RFC 2047 encoded and comes back decoded, with the raw form kept beside it in headers. The Date header gives both what was written and the instant it means. Both addresses are reported with the same syntax verdict the identity skills give.

An invoice with an attachment whose filename is a path traversal, read with addresses redacted

Inputjson
{
  "redactAddresses": true,
  "rawEmail": "From: Billing <[email protected]>\r\nTo: [email protected], \"Doe, John\" <[email protected]>\r\nSubject: Invoice 20260728\r\nContent-Type: multipart/mixed; boundary=\"mix\"\r\n\r\n--mix\r\nContent-Type: text/plain; charset=utf-8\r\n\r\nInvoice attached, due in 30 days.\r\n--mix\r\nContent-Type: application/pdf\r\nContent-Disposition: attachment; filename=\"../../invoices/20260728.pdf\"\r\nContent-Transfer-Encoding: base64\r\n\r\nSW52b2ljZSBib2R5IGJ5dGVz\r\n--mix--\r\n",
  "includeBody": "BOTH"
}
Expected outputjson
{
  "headers": [
    {
      "name": "From",
      "value": "<redacted>@northwind.example"
    },
    {
      "name": "To",
      "value": "<redacted>@acme.example, <redacted>@acme.example"
    },
    {
      "name": "Subject",
      "value": "Invoice 20260728"
    },
    {
      "name": "Content-Type",
      "value": "multipart/mixed; boundary=\"mix\""
    }
  ],
  "body": {
    "text": "Invoice attached, due in 30 days.",
    "truncated": false,
    "charset": "UTF-8"
  },
  "subject": "Invoice 20260728",
  "attachments": [
    {
      "contentDisposition": "attachment",
      "filename": "../../invoices/20260728.pdf",
      "index": 0,
      "contentType": "application/pdf",
      "filenameSafe": "20260728.pdf",
      "sizeBytes": 18
    }
  ],
  "addresses": {
    "from": [
      {
        "address": "<redacted>@northwind.example",
        "domain": "northwind.example",
        "valid": true
      }
    ],
    "to": [
      {
        "address": "<redacted>@acme.example",
        "domain": "acme.example",
        "valid": true
      },
      {
        "address": "<redacted>@acme.example",
        "domain": "acme.example",
        "valid": true
      }
    ],
    "cc": [],
    "bcc": [],
    "replyTo": []
  },
  "structure": {
    "contentType": "multipart/mixed",
    "parts": [
      {
        "contentType": "text/plain",
        "parts": []
      },
      {
        "contentType": "application/pdf",
        "parts": []
      }
    ]
  },
  "warnings": [],
  "parsed": "complete",
  "summary": {
    "sizeBytes": 445,
    "partCount": 2,
    "attachmentCount": 1,
    "redacted": true,
    "headerCount": 4,
    "depth": 1,
    "hasHtml": false,
    "hasText": true
  },
  "headerIndex": {
    "subject": "Invoice 20260728",
    "content-type": "multipart/mixed; boundary=\"mix\"",
    "from": "<redacted>@northwind.example",
    "to": "<redacted>@acme.example, <redacted>@acme.example"
  }
}
Tip
The attachment is described and its bytes are not returned. filename is echoed exactly as the sender wrote it, traversal included, because it is evidence; filenameSafe is the single path-free segment to use if you ever write the name down. Redaction has kept the domains and dropped the local parts in both the address list and the To and From headers, and the comma inside the quoted display name did not split that recipient into two.
  • Turn an inbound support mailbox into structured tickets, keeping the subject, the sender and the thread references
  • Route or triage a message by what is in its headers rather than by its body
  • List what a message carries — types, names and sizes — before deciding whether to fetch any of it
  • Follow a thread by Message-ID, In-Reply-To and References
  • Read a Received chain when you need to see how a message actually arrived

Avoid for

  • Sending a reply, which needs a transport we deliberately do not have
  • Deciding whether a message is spam or whether its sender is authentic
  • Getting the bytes of an attachment out of a message
  • Splitting a mailbox file, an mbox or a partial IMAP fetch into messages

Composable with

Safety & privacy

  • Runs without calling any external service.
  • Your payloads are never stored permanently.
  • Telemetry captures metadata only — never your payload contents.
  • We never train models on your payloads.
  • No human reviews your payloads.

Tags

messaginganalysisemailparsingtext