---
title: "Parsing Raw Email (.eml)"
canonical: "https://test.abhinandan.one/node-sdk-parsing-email"
markdown_url: "https://test.abhinandan.one/node-sdk-parsing-email.md"
publisher: "Primitive SDKs"
kind: "guide"
content_type: "reference"
category: "Node.js SDK"
description: "Parse raw MIME/.eml bytes into bodies, headers, and attachments with the Node SDK, then extract sender addresses strictly with parseFromHeader."
keywords: ["parseFromHeader", "parseFromHeaderLoose", "@primitivedotdev/sdk/parser", "parse.eml file", "ValidatedAddress", "ParsedAddress"]
last_modified: "2026-08-11T18:54:52.553889+00:00"
published_at: "2026-08-11T18:54:52.394319+00:00"
source_files:
  - "sdk-node/src/parser/address-parser.ts"
sections:
  - {anchor: "extract-a-validated-address-from-a-from-header", title: "Extract a validated address from a From header"}
  - {anchor: "parse-a-header-for-display-purposes", title: "Parse a header for display purposes"}
  - {anchor: "choosing-between-the-two-parsers", title: "Choosing between the two parsers"}
  - {anchor: "step-identify-what-the-parsed-address-controls", title: "Identify what the parsed address controls"}
  - {anchor: "step-handle-the-failure-path-explicitly", title: "Handle the failure path explicitly"}
  - {anchor: "step-lowercase-compare-dont-case-compare", title: "Lowercase-compare, don't case-compare"}
  - {anchor: "working-with-the-raw-email-body-and-attachments", title: "Working with the raw email body and attachments"}
  - {anchor: "sanitizing-inbound-html-for-rendering", title: "Sanitizing inbound HTML for rendering"}
  - {anchor: "next-steps", title: "Next steps"}
---

> Documentation index: https://test.abhinandan.one/llms.txt

# Parsing Raw Email (.eml)

Parse raw MIME bytes into structured bodies, headers, and attachments with the Node SDK's parser module, then extract addresses safely and sanitize HTML before rendering it.

Use the Node SDK's `parser` subpath when you need to work with raw MIME bytes directly: a `.eml` file on disk, a full raw email downloaded from [Signed Download Tokens](https://test.abhinandan.one/node-sdk-download-tokens.md), or a From header you need to validate for an authorization decision. Most webhook handlers never need this, [`primitive.receive(...)`](https://test.abhinandan.one/node-sdk-receiving-email.md) already gives you a normalized `ReceivedEmail` with `sender`, `text`, and `thread` extracted for you.

Reach for the parser module when:

- You downloaded the raw email via a download-token URL or `email.raw` and need to walk its MIME structure yourself.
- You have a From/Sender/Reply-To header string and need a strict, security-safe parse before gating an action on it.
- You're rendering inbound HTML in a browser and need it sanitized first.

> **Note:** `@primitivedotdev/sdk/parser` is Node-only (it depends on `nodemailer`'s address parser and `validator`). It is not exported from the Workers-safe `/api` or `/webhook` subpaths. Don't import it into a Primitive Function handler.

## Extract a validated address from a From header

`parseFromHeader` is the strict parser for security-bearing contexts, allowlist gates, permission grants, anything that decides "did this really come from `example.com`." Import it from `@primitivedotdev/sdk/parser`:

```typescript
import { parseFromHeader } from "@primitivedotdev/sdk/parser";

const result = parseFromHeader('"Alice" <alice@example.com>');

if (result.ok) {
  console.log(result.value.address); // "alice@example.com"
} else {
  console.log(result.reason); // e.g. "multiple_addresses"
}
```

`parseFromHeader` returns a typed `ParseFromHeaderResult` whose `ok: false` branch carries a stable `ParseFromHeaderFailureReason`; the full result shape, rejection reasons, and normalization rules are documented in [Raw MIME Address Parsing (parser/address)](https://test.abhinandan.one/node-sdk-parsing-email/node-sdk-address-parsing.md).

> **Warning:** Do not build your own allowlist check by regexing the raw From header. A header like `From: "trusted@example.com" <x@evil.com>` puts an allowlisted address in the *display name* while the real address is `evil.com`. `parseFromHeader` avoids this because it never treats the display name as the address.

> **Tip:** For deciding whether an inbound email is authentic (SPF/DKIM/DMARC), use [`isTrustedSender`](https://test.abhinandan.one/node-sdk-email-authenticity.md) instead, it's the purpose-built domain-anchored trust check. `parseFromHeader` is for extracting *and validating the shape of* an address string you already have; it does not check authentication.

## Parse a header for display purposes

When you're rendering a "from" field in a UI or log line, not gating access, use the lenient parser instead:

```typescript
import { parseFromHeaderLoose } from "@primitivedotdev/sdk/parser";

const parsed = parseFromHeaderLoose('Alice Smith <alice@example.com>, bob@example.com');

if (parsed) {
  console.log(parsed.address); // "alice@example.com" (first address)
  console.log(parsed.name);    // "Alice Smith"
}
```

`parseFromHeaderLoose` returns `ParsedAddress | null`, taking the first parseable address (flattening group syntax) and including the display name; see [Raw MIME Address Parsing (parser/address)](https://test.abhinandan.one/node-sdk-parsing-email/node-sdk-address-parsing.md) for the full behavior comparison.

> **Warning:** Never use `parseFromHeaderLoose` for permission gates or any decision that grants access. Its `name` field can include address-parser recovery artifacts (trailing tokens, garbage before the address), treat it as opaque display text only. Use `parseFromHeader` for anything security-bearing.

## Choosing between the two parsers

### 1. Identify what the parsed address controls

If the result gates an action (reply-from validation, sender allowlisting, authorization of any kind), you need `parseFromHeader`. If the result only appears in a UI, log, or notification, `parseFromHeaderLoose` is fine.

### 2. Handle the failure path explicitly

`parseFromHeader` never throws, it returns `{ ok: false, reason }`. Branch on `reason` to give a specific error rather than a generic "invalid sender" message:

```typescript
if (!result.ok) {
  switch (result.reason) {
    case "multiple_addresses":
      // reject: ambiguous sender identity
      break;
    case "too_long":
      // reject: probable header injection or corrupt feed
      break;
    default:
      // reject: malformed or empty
      break;
  }
}
```

### 3. Lowercase-compare, don't case-compare

Both parsers already lowercase the returned `address`. Compare against a lowercased allowlist value, not the original-case header string.

## Working with the raw email body and attachments

For most inbound mail you never need to walk the MIME tree yourself: `primitive.receive(...)` already returns a normalized `ReceivedEmail` with `text`, `thread`, and the attachment list, as described in [Receiving Inbound Email](https://test.abhinandan.one/node-sdk-receiving-email.md). Once you have those attachments, [Bundling Attachments](https://test.abhinandan.one/node-sdk-attachment-bundling.md) covers packaging them into a single archive. The helpers documented on this page are the address parsers from the same `parser` subpath.

## Sanitizing inbound HTML for rendering

Treat any inbound HTML body as attacker-controlled input and run it through a sanitizer before injecting it into the DOM. See [Receiving Inbound Email](https://test.abhinandan.one/node-sdk-receiving-email.md) for the fields available on `ReceivedEmail`, then handle the HTML the same way you would any other untrusted markup.
