How to Parse Email Attachments

Access filename, size, content type, and downloadable content for every email attachment. JsonHook includes structured attachment metadata in every webhook payload.

Table of Contents
  1. Overview
  2. Prerequisites
  3. Step-by-Step Instructions
  4. Code Example
  5. Common Pitfalls

Overview

Email attachments present a parsing challenge: they are base64-encoded binary data embedded in a MIME multipart message, sometimes nested multiple levels deep, sometimes encoded as inline images with Content-ID references, and sometimes disguised as body parts by non-conforming mail clients. Extracting them reliably from raw MIME requires careful boundary parsing and encoding handling.

JsonHook handles all of this transparently. Every inbound email is parsed for attachments, and each attachment is described in the webhook payload's email.attachments array. For each attachment you get:

  • filename — the original filename from the email
  • contentType — the MIME type (e.g., application/pdf, image/png)
  • size — file size in bytes
  • contentId — the Content-ID for inline images (null for regular attachments)

Attachment binary content is not included inline in the webhook payload (to keep payload sizes manageable), but is available for download via the JsonHook Attachments API on Pro plans.

Prerequisites

Requirements for working with email attachments via JsonHook:

  • A JsonHook inbound address and webhook endpoint (any plan)
  • For downloading attachment content: a Pro plan and your API key
  • Storage to save downloaded attachment content (local disk, S3, GCS, etc.)

To test attachment parsing, send an email to your JsonHook address with one or more attached files (PDF, image, CSV, etc.) before building your handler. Inspect the resulting webhook payload to see the attachment array.

Process Email Attachments in Your Pipeline

Attachment metadata included in every payload. Content download available on Pro.

Get Free API Key

Step-by-Step Instructions

Handle email attachments in your webhook pipeline:

  1. Access the attachments array from the webhook payload:
    const attachments = payload.email.attachments;
    // Each attachment: { filename, contentType, size, contentId, id }
  2. Filter by content type to process only the attachment types your application cares about:
    const pdfs = attachments.filter(a => a.contentType === "application/pdf");
    const images = attachments.filter(a => a.contentType.startsWith("image/"));
  3. Validate attachment sizes before downloading — reject oversized files to prevent abuse:
    const MAX_SIZE = 10 * 1024 * 1024; // 10 MB
    const valid = attachments.filter(a => a.size <= MAX_SIZE);
  4. Download attachment content (Pro plan) via the Attachments API:
    GET https://api.jsonhook.com/v1/deliveries/{deliveryId}/attachments/{attachmentId}
    Authorization: Bearer YOUR_API_KEY
    The response is the raw binary file content with the correct Content-Type header.
  5. Store the file in your chosen storage backend (S3, filesystem, database BLOB, etc.).
  6. Process the content as needed — OCR a PDF, resize an image, parse a CSV, etc.

Code Example

This Node.js handler receives an email, finds all PDF attachments, downloads them, and uploads to S3:

import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
import fetch from "node-fetch";

const s3 = new S3Client({ region: "us-east-1" });

async function processEmailAttachments(payload: any) {
  const { email, deliveryId } = payload;

  const pdfs = (email.attachments || []).filter(
    (a: any) => a.contentType === "application/pdf"
  );

  for (const attachment of pdfs) {
    // Download from JsonHook (Pro plan)
    const res = await fetch(
      `https://api.jsonhook.com/v1/deliveries/${deliveryId}/attachments/${attachment.id}`,
      { headers: { Authorization: `Bearer ${process.env.JSONHOOK_API_KEY}` } }
    );

    if (!res.ok) {
      console.error(`Failed to download ${attachment.filename}`);
      continue;
    }

    const buffer = Buffer.from(await res.arrayBuffer());

    // Upload to S3
    await s3.send(new PutObjectCommand({
      Bucket: "my-email-attachments",
      Key: `${deliveryId}/${attachment.filename}`,
      Body: buffer,
      ContentType: attachment.contentType,
    }));

    console.log(`Saved ${attachment.filename} (${attachment.size} bytes) to S3`);
  }
}

Common Pitfalls

Watch for these attachment handling issues:

  • Trusting the filename from the email. Attachment filenames come from the sender and can contain path traversal characters (../../etc/passwd) or malicious extensions. Always sanitize filenames before using them in filesystem operations.
  • Not validating the content type. The contentType in the attachment metadata comes from the MIME header the sender included — it may not match the actual file content. For security-sensitive pipelines, validate file content server-side (e.g., libmagic, file signatures).
  • Processing large attachments synchronously. Downloading and processing a large attachment in the webhook handler can cause a timeout. Download attachment content asynchronously in a background job and acknowledge the webhook immediately.
  • Ignoring inline images. Inline images (with a contentId) appear in attachments with a non-null contentId. If you are archiving all attachments, include inline images. If you only want user-attached files, filter where contentId === null.
  • Not enforcing attachment count or total size limits. An adversarial sender could attach many large files. Set hard limits on the number and total size of attachments you will process per email.

Frequently Asked Questions

Is attachment content included in the webhook payload?

Attachment metadata (filename, contentType, size, contentId) is included in every webhook payload at no extra charge. The actual binary content of attachments is downloadable via a separate API endpoint on Pro plans. This keeps webhook payload sizes manageable while still giving you full attachment access.

What is the maximum attachment size JsonHook will handle?

JsonHook accepts emails up to 25 MB total including all attachments. Individual attachments can be up to 25 MB, subject to the total email size limit. Emails exceeding 25 MB are rejected at the SMTP level and the sender receives a bounce.

How long is attachment content stored before it is deleted?

Attachment content is retained for 7 days on the Starter plan and 30 days on the Pro plan. After the retention period, attachment content is permanently deleted. Delivery metadata (including attachment metadata) is retained longer — see your plan details for exact retention periods.

Can I receive password-protected ZIP or encrypted attachments?

JsonHook receives and stores encrypted or password-protected attachments like any other file — they appear in the attachments array with their filename and content type. Decryption must happen in your application after downloading the attachment content; JsonHook does not decrypt attachment content.