Procesamiento automático de adjuntos de correo electrónico

Procesar archivos .eml y .msg con la API de MaraDocs. Extraer, validar y procesar adjuntos de correo electrónico como imágenes o PDFs. Soporte recursivo para correos dentro de correos.

Martin Kurtz
APICorreo electrónicoProcesamiento de documentosDesarrolladores
Procesamiento automático de adjuntos de correo electrónico

Los correos electrónicos entrantes contienen frecuentemente documentos como adjuntos: imágenes, PDFs y, a veces, archivos .eml o .msg anidados. En nuestro despacho, un solo correo de cliente podía contener una docena de fotos de un informe de accidente, un PDF pericial y un correo reenviado con sus propios adjuntos. Una API de procesamiento de adjuntos de correo que extrajera, validara y dirigiera cada adjunto al flujo correcto ahorraría horas de trabajo manual.

Las imágenes deben derivarse a extracción de documentos u OCR, los PDFs a combinación o compresión, y los correos anidados deben procesarse recursivamente.

Por qué desarrollar una solución propia de procesamiento de adjuntos de correo lleva semanas

Quien quiera construir esto por sí mismo descubre rápidamente que analizar .eml y .msg requiere mail-parser, extract-msg o similares. Luego hay que decodificar MIME, procesar mensajes anidados, extraer datos binarios, reconocer formatos (magika, firmas de archivo), realizar análisis antivirus y bifurcar según lógica de imagen/PDF. Cada paso tiene sus propias fuentes de error: encabezados defectuosos, codificaciones y correos anidados con sus propios adjuntos. Construir internamente una API robusta de procesamiento de adjuntos de correo lleva semanas.

Cómo la API de procesamiento de adjuntos de correo de MaraDocs lo resuelve en minutos

La API de MaraDocs valida archivos .eml y .msg en una sola llamada. Se obtienen identificadores de adjuntos estructurados, cada uno con información de tipo. Desde ahí se puede bifurcar según operaciones de imagen o PDF: validar por tipo, luego aplicar extracción de documentos, OCR, composición o compresión. La API soporta procesamiento recursivo de correos adjuntos dentro de correos.

Flujo de procesamiento de adjuntos de correo: Cargar, Validar, Extraer, Derivar

Cargar archivo de correo, llamar a email.validate. La respuesta contiene identificadores de adjuntos, cada uno con tipo de contenido y metadatos. Para cada adjunto, verificar el tipo (imagen, PDF o correo anidado). Validar el adjunto con img.validate o pdf.validate, luego encadenar al flujo: flow.ocrImg para imágenes, pdf.compose para combinar PDFs, pdf.optimize para compresión. Los correos anidados pueden validarse nuevamente para extraer sus adjuntos recursivamente. Todo el flujo se ejecuta del lado del servidor; los identificadores se pasan entre pasos sin descargar y volver a cargar.

Get your API key in under a minute

Register for a free account and get your API key in under a minute. Of course we'll provide you with some developer credits.

Try MaraDocs API now →

Lo que diferencia a MaraDocs: Workspaces, vista web y derecho de protección de datos alemán

La mayoría de las APIs de documentos obligan a descargar cada adjunto, volver a cargarlo en otro lugar y rastrear identidades a través de múltiples pasos. En MaraDocs, todos los archivos —el correo y sus adjuntos— viven en el mismo workspace. Validar correo, iterar identificadores de adjuntos y pasarlos a endpoints img/pdf/flow. Los identificadores fluyen; los datos permanecen del lado del servidor. No hay ciclos de descarga-carga para cada adjunto.

Si un correo anidado tiene una estructura inesperada o un adjunto necesita revisión manual, se puede abrir app.maradocs.io con el secreto del workspace para inspeccionar y reorganizar archivos. Los usuarios obtienen control manual completo cuando la automatización alcanza sus límites.

El procesamiento se realiza en Alemania (Maramia GmbH), con cifrado en reposo y en tránsito. Los workspaces expiran después de 7 días. Ningún dato sale de la UE. Para procesamiento de correos sensible a la protección de datos, esto es relevante.

Código TypeScript para extraer adjuntos de correo

Referencia API: data/upload, email/validate, img/validate, pdf/validate, flow.ocrImg, data/download/pdf

import { MaraDocsClient } from "@maramia/maradocs-sdk-ts";
import { okEmail } from "@maramia/maradocs-sdk-ts/models/email";
import { okImg } from "@maramia/maradocs-sdk-ts/models/img";
import { okPdf } from "@maramia/maradocs-sdk-ts/models/pdf";

const client = new MaraDocsClient({ workspaceSecret: workspace_secret });

// Cargar y validar correo
const uploaded = await client.data.upload(emailFile);
const validated = await client.email.validate({
  unvalidated_file_handle: uploaded.unvalidated_file_handle,
});
const email = okEmail(validated);

const pdfHandles: string[] = [];
for (const att of email.attachments) {
  if (att.content_type?.startsWith("image/")) {
    const imgVal = await client.img.validate({
      unvalidated_file_handle: att.unvalidated_file_handle,
    });
    const imgHandle = okImg(imgVal);
    const pdfHandle = await client.flow.ocrImgHandle(imgHandle);
    pdfHandles.push(pdfHandle);
  } else if (att.content_type === "application/pdf") {
    const pdfVal = await client.pdf.validate({
      unvalidated_file_handle: att.unvalidated_file_handle,
    });
    pdfHandles.push(okPdf(pdfVal));
  }
}
// Combinar y descargar
const composed = await client.pdf.compose({
  pdfs: pdfHandles.map((pdf_handle) => ({ pdf_handle })),
});
const blob = await client.data.downloadPdf({ pdf_handle: composed.pdf_handle });

Código Python para extracción de adjuntos de correo

Referencia API: data/upload, email/validate, img/validate, pdf/validate, pdf/compose, data/download/pdf

# pip install python-decouple requests
"""Extract PDF attachments from an email using MaraDocs. Set ACCOUNT_SECRET in .env or environment."""

import sys
import time
from pathlib import Path

import requests
from decouple import config

API_URL = "https://api.maradocs.io/v1"


def create_workspace() -> dict:
    """Create a workspace and return auth headers."""
    r = requests.post(
        f"{API_URL}/workspace",
        headers={"Authorization": f"Bearer {config('ACCOUNT_SECRET')}"},
        json={"subaccount": None},
    )
    ws = r.json()
    return {"Authorization": f"Bearer {ws['workspace_secret']}"}


def upload_file(path: Path, auth: dict) -> dict:
    """Upload a file via two-step flow (signed URL + S3 POST). Returns unvalidated_file_handle."""
    data = path.read_bytes()
    resp = requests.post(
        f"{API_URL}/data/upload",
        headers={**auth, "Content-Type": "application/json"},
        json={"name": path.name, "size": len(data)},
    ).json()
    requests.post(
        resp["post_url"],
        data=resp.get("post_header", {}),
        files={"file": (path.name, data, "message/rfc822")},
    )
    return resp["unvalidated_file_handle"]


def run_job(path: str, payload: dict, auth: dict, timeout: int = 60) -> dict:
    """Run a job and poll until complete. Returns the result (unwraps response if present)."""
    url = f"{API_URL}/{path}"
    r = requests.post(url, headers=auth, json=payload).json()
    job_id = r["job_id"]
    start = time.time()
    while time.time() - start < timeout:
        poll_r = requests.get(f"{url}/{job_id}", headers=auth)
        if poll_r.status_code == 200:
            return poll_r.json()
    raise TimeoutError(f"Job {path} timed out")


def pdf_handles_from_email(email_handle: dict) -> list:
    """Recursively extract all pdf_handles from an email_handle (handles nested emails)."""
    handles = []
    for att in email_handle.get("attachments", []):
        media = att.get("media_type") or {}
        mt = media.get("media_type", "") if isinstance(media, dict) else ""
        validated = att.get("validated") or {}

        if mt == "application/pdf":
            resp = validated.get("response", {})
            if pdf_h := resp.get("pdf_handle"):
                handles.append(pdf_h)
        elif mt == "message/rfc822":
            nested = (validated.get("response") or {}).get("email_handle")
            if nested:
                handles.extend(pdf_handles_from_email(nested))
    return handles


def download_pdf(pdf_handle: str, auth: dict) -> bytes:
    """Request signed URL and fetch PDF bytes."""
    r = requests.post(
        f"{API_URL}/data/download/pdf",
        headers=auth,
        json={"pdf_handle": pdf_handle},
    )
    info = r.json()
    dl = requests.get(info["url"], headers=info.get("headers", {}))
    return dl.content


def main() -> None:
    email_path = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("input_email.eml")
    auth = create_workspace()
    handle = upload_file(email_path, auth)
    email_data = run_job("email/validate", {"unvalidated_file_handle": handle}, auth)
    email_handle = email_data["response"]["email_handle"]
    pdf_handles = pdf_handles_from_email(email_handle)

    if not pdf_handles:
        sys.exit("No PDF attachments found")

    composed = run_job(
        "pdf/compose",
        {"pdfs": [{"pdf_handle": h} for h in pdf_handles]},
        auth,
    )
    pdf_handle = composed["pdf_handle"]
    Path("attachments.pdf").write_bytes(download_pdf(pdf_handle, auth))
    print("attachments.pdf created")


if __name__ == "__main__":
    main()

Resumen y próximos pasos

Una API de procesamiento de adjuntos de correo que extrae, valida y deriva adjuntos está disponible. MaraDocs se encarga del análisis antivirus, reconocimiento de formatos y análisis recursivo de correos. Para automatización completa, combinar con escáner de documentos, procesamiento de PDF y reconocimiento de texto.


Pruébalo ahora: API de MaraDocs | SDK TypeScript


Suscríbase ahora al boletín

Manténgase al día con nosotros y reciba las últimas noticias, artículos y recursos por correo electrónico.