Skip to main content

AI Agent Reference

This page summarizes the public API of postal-mime on one page, for AI coding assistants and anyone who wants the whole surface at a glance. Each section links to the canonical page with the details.

Documented release: postal-mime 4.1.4

Package​

Installnpm install postal-mime
RuntimesNode.js 20+, browsers, Web Workers, Deno (npm:postal-mime), Bun, Cloudflare Workers
DependenciesNone
ES moduleimport PostalMime, { addressParser, decodeWords } from 'postal-mime' (dist/esm)
CommonJSconst PostalMime = require('postal-mime'), returns the class with addressParser and decodeWords attached (dist/cjs)
Browser without bundler./node_modules/postal-mime/dist/esm/postal-mime.js
TypesBundled, no @types package. CommonJS TypeScript: import PostalMime = require('postal-mime')

Exports​

ExportSignatureDetails
PostalMime (default)PostalMime.parse(email: RawEmail, options?: PostalMimeOptions): Promise<Email>PostalMime
new PostalMime(options?), then parser.parse(email): Promise<Email>, once per instance
addressParseraddressParser(value: string, options?: { flatten?: boolean }): Address[]addressParser()
decodeWordsdecodeWords(value: string): stringdecodeWords()
TypesEmail, Address, Mailbox, AddressGroup, Header, HeaderLine, Attachment, AttachmentDisposition, AttachmentEncoding, PostalMimeOptions, AddressParserOptions, RawEmail, PostalMimeError, ErrorCodeTypes

RawEmail is string | ArrayBuffer | ArrayBufferView | Blob | ReadableStream<Uint8Array>. Pass bytes rather than a decoded string when the message may contain non UTF-8 text. A stream is read fully into memory before parsing.

Options​

OptionTypeDefaultEffect
rfc822Attachmentsbooleanfalsemessage/rfc822 parts without Content-Disposition become attachments
forceRfc822AttachmentsbooleanfalseEvery message/rfc822 part becomes an attachment. Automatic for delivery and feedback reports
attachmentEncoding'arraybuffer' | 'base64' | 'utf8''arraybuffer'Type of Attachment.content
maxNestingDepthnumber256EMAXNESTINGDEPTH when exceeded
maxHeadersSizenumber2097152Bytes, whole message; EMAXHEADERSIZE
maxPartCountnumber10000Whole message; EMAXPARTCOUNT
maxRfc822NestingDepthnumber10Deeper nested messages become attachments with rfc822DepthExceeded: true; 0 disables inline parsing
maxForwardedAddressesnumber10000Addresses of inline forwards; EMAXFORWARDEDADDRESSES

Limit options must be non-negative integers, and maxPartCount a positive one, or EINVALIDOPTION is raised; 0 means zero. Details: Parser Options.

Email object​

PropertyTypeNotes
headers{ key, originalKey, value }[]Always present. Top-level headers in order, duplicates kept. key lowercase with whitespace around the name kept, value unfolded, not decoded. The block ends at the first line that is neither a header field nor a fold; what follows is body text
headerLines{ key, line }[]Always present. Raw lines, folds joined with LF
from, senderAddressFirst occurrence
to, cc, bcc, replyToAddress[]Every occurrence, in order
deliveredTo, returnPathstringAddress only
subject, messageId, inReplyTo, referencesstringFirst occurrence, encoded words decoded
datestringISO 8601, or the raw value if unparseable
otp{ code, origin }From a single top-level OTP-Token header (draft-goto-otp-token) with a canonical https (or loopback http) origin, absent otherwise. Not authenticated, verify DKIM for the origin domain. Experimental, follows the draft
text, htmlstringLF line endings. Absent when the message has no part of either type; filled by conversion when the message mixes types. html is not sanitized
attachmentsAttachment[]Always present

Optional properties are absent, not null, when the message has no value. Address is { name, address } (mailbox) or { name, group: Mailbox[] } (group); test addr.group to tell them apart. Details: Email Object.

Attachment object​

PropertyTypeNotes
filenamestring | nullDecoded, sender-controlled: sanitize before using as a path
mimeTypestringLowercase, sender-controlled, not validated (may hold whitespace, quotes or path separators)
dispositionstring | nullLowercased Content-Disposition token
relatedtrue or absentInline resource in multipart/related with a Content-ID
contentIdstring or absentUsually with angle brackets, not validated (may lack them or repeat); cid: URLs omit them
descriptionstring or absentDecoded Content-Description
methodstring or absentUppercased calendar method, eg. REQUEST
rfc822DepthExceededtrue or absentUnparsed nested message; its content is not reflected anywhere else
contentArrayBuffer | Uint8Array | stringArrayBuffer by default, Uint8Array for text/calendar and application/ics, a string with base64/utf8
encoding'base64' | 'utf8' or absentSet exactly when content is a string

Errors​

Every thrown error has code (since 4.1.0). PostalMime.parse() always rejects rather than throwing; new PostalMime() throws EINVALIDOPTION synchronously.

CodeTypeMeaning
EINVALIDOPTIONTypeErrorInvalid option value
EINVALIDINPUTTypeErrorUnsupported input type, or non-byte stream chunk
EPARSERREUSEDErrorparse() called twice on one instance
EMAXNESTINGDEPTH, EMAXHEADERSIZE, EMAXPARTCOUNT, EMAXFORWARDEDADDRESSESErrorA security limit was exceeded

Malformed messages do not throw. null, undefined and '' parse to an empty message. A malformed message is read the way Python's email package and mailparser read it: a message/rfc822 part encoded as base64 or quoted-printable is an attachment, format=flowed applies to text/plain only, a multipart whose boundary holds a non-ASCII character is plain text, a quoted "base64" transfer encoding is not decoded, and only utf-16, utf16, utf_16, utf-16le and utf-16be are read as UTF-16 (since 4.1.4). Details: Errors, Security.

Common mistakes to avoid​

  • Reusing a PostalMime instance. Use PostalMime.parse() for each message.
  • Decoding a message to a string before parsing it. Pass the bytes.
  • Reading message.raw in a Cloudflare Email Worker twice. It is a stream; read it once into an ArrayBuffer if it is needed again.
  • Inserting html into a page without a sandboxed iframe or a sanitizer, or text without escaping.
  • Using filename as a file path, or contentId in a RegExp, without sanitizing it.
  • Reading content.byteLength after choosing the base64 or utf8 encoding, where content is a string.
  • Treating attachments as complete in a scanner without checking rfc822DepthExceeded.
  • Passing an options object from untrusted input to the parser.
  • Matching on err.message instead of err.code.

See Also​