Blog/Use cases

Convert email to PDF with an eml to pdf api

Use cases··6 min read

To turn inbound email into one PDF with an eml to pdf api, POST the message to /v1/convert. Send file plus sourceFilename whose extension is .eml or .msg, and set to to pdf. LibreOffice is the default engine. Chromium on POST /v1/pdf does not open Office or email files. Product: https://relaypdf.com/office-to-pdf. Convert docs: https://relaypdf.com/docs/convert. REST list: https://relaypdf.com/docs.

There is no separate email endpoint. ConvertRequest is the same body used for Word, Excel, and PowerPoint. The office-to-pdf page lists supported sources as Word, Excel, PowerPoint, text, images, email files, and other LibreOffice-openable formats. Targets are pdf, docx, xlsx, html, or png. This how-to covers email files to pdf only.

Why /v1/convert, not /v1/pdf

POST /v1/pdf is Chromium: HTML, a public URL, Markdown, or a published Handlebars template. The convert docs say not to send .docx through that path. Email files are on the Office/LibreOffice path for the same reason. wkhtmltopdf is the other convert engine. It only supports html or url to pdf, and it can set options.toc. Do not send .eml or .msg with engine wkhtmltopdf. Leave engine at the default, libreoffice, or omit the field.

Provide exactly one of html, url, or file. file requires sourceFilename with an extension. That extension is how the document worker picks a filter. Use ticket.eml for RFC 822 / MIME saves and ticket.msg for Outlook message files. filename names the output. Mixing html and file in one body is invalid_request.

You haveSend
A saved .emlfile + sourceFilename ending .eml; to: pdf
A saved .msgfile + sourceFilename ending .msg; to: pdf
Word / Excel / PPTSame convert body; change the extension
HTML you authoredPOST /v1/pdf (Chromium), not convert
Several finished PDFsPOST /v1/pdf/merge after convert
Need a thumbnailto: png on convert

Documented ConvertRequest fields

OpenAPI names the body ConvertRequest. Sources: html (string), url (URI), file (base64, optional data-URI prefix). sourceFilename is required when you send file. filename is the output name. to enum: pdf (default), docx, xlsx, html, png. engine enum: libreoffice (default) or wkhtmltopdf. response is binary, url, or async. callbackUrl is an https URI. options has one published key: toc (boolean), and that is for wkhtmltopdf, not email.

The schema does not list .eml or .msg as an enum. Support is the product-page source list (email files / LibreOffice-openable). Do not invent embedAttachments, convertAttachments, or a filter name. Do not invent a dedicated POST /v1/pdf/from-email.

ConstraintPublished valueSource
EndpointPOST /v1/convertdocs hub; OpenAPI
Email sourcesemail files among LibreOffice-openable formats/office-to-pdf
Exclusive inputexactly one of html, url, or file/docs/convert
filebase64; optional data URI; needs sourceFilenameConvertRequest
sourceFilenamerequired with file; must have an extension/docs/convert
topdf | docx | xlsx | html | png (default pdf)ConvertRequest
enginelibreoffice (default) | wkhtmltopdfConvertRequest
wkhtmltopdfhtml or url to pdf only/office-to-pdf; /docs/convert
Price$0.04 LibreOffice; $0.025 wkhtmltopdf/pricing
Output modesbinary | url | asyncdocs hub
url-mode downloadGET /v1/files/:id, 24 hours, no keydocs hub
Cold worker503 convert_unavailable, not billed/docs/errors
Byte capNot published; 413 payload_too_large/docs/errors

Convert a .eml

Base64 the bytes. Put the real extension on sourceFilename. Default response is binary: file bytes plus x-relaypdf-id, x-relaypdf-size, and content-disposition. Set response to url for JSON with a 24-hour GET /v1/files/:id that needs no key. Set response to async for HTTP 202 and poll GET /v1/jobs/:id, or supply callbackUrl.

curl -X POST https://api.relaypdf.com/v1/convert
  -H "Authorization: Bearer $RELAYPDF_API_KEY"
  -H "Content-Type: application/json"
  -d '{
    "file": "UEsDB...",
    "sourceFilename": "ticket.eml",
    "to": "pdf",
    "filename": "ticket.pdf"
  }'
  --output ticket.pdf

The product curl for Word uses the same shape (file, sourceFilename letter.docx, to pdf). Only the extension and output name change. The placeholder UEsDB... is the docs example prefix; a real .eml is MIME text and will look different after base64.

Convert a .msg

Outlook .msg is the same request. Change sourceFilename. engine stays libreoffice. Do not add html. Do not switch to POST /v1/pdf.

curl -X POST https://api.relaypdf.com/v1/convert
  -H "Authorization: Bearer $RELAYPDF_API_KEY"
  -d '{
    "file": "0M8R4KGx...",
    "sourceFilename": "ticket.msg",
    "to": "pdf",
    "filename": "ticket.pdf"
  }'
  --output ticket.pdf

url is a documented exclusive source when the message already lives on a public HTTPS host. Private, loopback, and metadata hosts are url_not_allowed. Query-string API keys are rejected. Prefer file when the inbound worker already holds the bytes.

Python, CLI, and MCP

Python 0.1.x: convert.from_path(path, **extra) POSTs /v1/convert. REST names stay camelCase. convert.create(**input) is the raw body. convert.from_html is HTML to docx/xlsx/pdf, not email. convert.wkhtml is the wkhtmltopdf helper and is the wrong engine here. file may be bytes. The published Office example is client.convert.from_path("letter.docx", to="pdf"). Swap the path.

CLI: relaypdf convert letter.docx --to pdf --out letter.pdf. Same command, different path: relaypdf convert ticket.eml --to pdf --out ticket.pdf. MCP lists convert. Agents should run npx @relaypdf/cli setup and wait for browser approval instead of asking for a pasted key.

import os
from relaypdf import RelayPDF
client = RelayPDF(api_key=os.environ["RELAYPDF_API_KEY"])
pdf = client.convert.from_path("ticket.eml", to="pdf", filename="ticket.pdf")
pdf.save("ticket.pdf")

For a long mailbox dump, use response async. jobs.wait polls GET /v1/jobs/:id (default interval 1000 ms, timeout 120000 ms in the Python client). files.download does not send the API key. LibreOffice convert is $0.04 per successful job on the live rate card.

job = client.convert.from_path(
    "ticket.msg",
    to="pdf",
    filename="ticket.pdf",
    response="async",
)
done = client.jobs.wait(job.id)
file = client.files.download(done["id"])
file.save("ticket.pdf")

Inbound email to one PDF

A typical loop is: receive mail, persist the raw .eml or .msg, POST /v1/convert, store the PDF. The API does not fetch IMAP or Exchange for you. It does not claim to flatten attachments into extra pages, embed them as PDF attachments, or merge nested .eml files. Those flags are not on ConvertRequest. If you need a packet of several converted files, convert each source, then POST /v1/pdf/merge with two to twenty FileSource items.

Sibling Office how-to: https://relaypdf.com/blog/docx-to-pdf-api (same convert body, Word extension). After you have a PDF you can protect (POST /v1/pdf/protect, $0.005), stamp (POST /v1/pdf/stamp), or extract pages. Do not invent print-area, landscape, or header options on convert; those belong to Chromium options on POST /v1/pdf.

Limits, errors, and billing

LibreOffice convert is $0.04. Failed jobs are never billed. convert_unavailable is HTTP 503 when the document worker is cold or unreachable — retry; not billed. invalid_request covers missing sourceFilename, more than one of html/url/file, or a bad to/engine. processing_failed is a 502 on a tool or job failure. payload_too_large is 413; no megabyte cap is published. payment_required is an empty wallet. rate_limited includes Retry-After. Trial is $5 and 20/min; funded or auto-reload is 60/min; burst is 5 / 10s (SDK READMEs).

Branch on error.code, not the status phrase. The envelope is { error: { code, message } }. SDKs throw RelayPDFError with status, code, message, and retryAfter when present.

What this is not

This is not a mailbox product, not OCR, not e-sign, not PDF/A, and not a claim about HIPAA. It is not Chromium print CSS. It is not wkhtmltopdf. The public schema has no attachment policy fields. Do not copy competitor email-to-PDF flags into this body. url-mode downloads are public for 24 hours. If the archive must stay closed, protect the PDF after convert.

If the source is HTML you control, use POST /v1/pdf. If the source is already a PDF, skip convert. If you only need a screenshot, that is POST /v1/images, a different product.

Ship it

Use the eml to pdf api when the source is a saved .eml or .msg and the output should be one PDF. POST /v1/convert. file plus sourceFilename. to pdf. engine libreoffice. $0.04 on success. Start at https://relaypdf.com/office-to-pdf. Field list: https://relaypdf.com/docs/convert and https://relaypdf.com/openapi.json. Auth is a bearer key on https://api.relaypdf.com.

Ready to generate?

One API for HTML, Markdown, URLs, and Office. REST, SDK, CLI, or MCP.