Skip to main content

Convert the Email Body and Every Attachment into One Consolidated PDF in Power Automate: A 7-Step Outlook + PDF4me Workflow

· 25 min read
SEO and Content Writer

The painful part of email-driven workflows is not the email, it is everything that comes with it. The body holds the context (who sent it, why, what to do), the attachments hold the artefacts (the invoice, the form, the signed contract), and storing them separately means future-you needs three clicks to reconstruct the message later. This walkthrough builds the exact pattern in Power Automate with PDF4me Convert to PDF, PDF4me Merge multiple PDF files, Outlook, Dropbox, and a single Apply to each loop. Seven flow actions. One incoming email lands. One consolidated PDF goes out, body first then attachment by attachment, timestamped and dropped into your archive folder automatically.

The flow at a glance
1. When a new email arrives (V3)
Outlook trigger on To [email protected]. Include Attachments Yes, Only with Attachments Yes.
2. Initialize variable
Name email, Type Array. Buffer for every PDF binary the flow produces.
3. Convert to PDF Body
PDF4me action. Content triggerOutputs body/body (the HTML body), File Name email-body.html. Returns the body as PDF.
4. Append to array (email body)
Push base64ToBinary on the converted body PDF onto the email array. This is page 1 of the final document.
5. Apply to each attachment
Loop triggerOutputs body/attachments. Convert each to PDF, then Append to array with base64ToBinary.
6. Merge multiple PDFs
PDF4me action. Body/docContent the email array, Output File Name Email Attachment Merge Output.pdf.
7. Create file
Dropbox /blog data/merge blog template/output. File Name concat(merge-, formatDateTime(utcNow(), yyyyMMdd-HHmmss), .pdf).
The short version

An Outlook When a new email arrives (V3) trigger fires the moment a message lands at [email protected]. An array variable named email opens up. PDF4me Convert to PDF turns the HTML body into a PDF first, the converted body is pushed onto the array as page 1. An Apply to each walks every attachment (two DOCX files in this run: 3Page.docx, Docx_2Pages.docx), converts each to PDF, and appends in turn. PDF4me Merge multiple PDF files consumes the whole array and emits one consolidated PDF. Dropbox Create file writes it back as merge-20260611-103323.pdf, a timestamped name that never collides. Body first, attachments in order, one PDF, one folder, audit-ready.

Why-Based Q&A

Why convert the body to PDF first? The body is the context: who sent it, why, what to do. Future-you reading the consolidated PDF wants page 1 to set the scene before the attachments roll in. Converting triggerOutputs()?['body/body'] (the HTML body) into a PDF first means the merge action sees the message itself as the lead document, not as a separate text dump.

Why one array variable for both body and attachments? The merge action wants a single ordered collection. Initialize once, push the body in step 4, push each converted attachment in step 5, and the array is exactly the document order you want. Two separate arrays would force a Compose action later to concatenate them, which is just more wiring for the same outcome.

Why base64ToBinary on every push? PDF4me Convert to PDF returns its output as a base64 string wrapped on $content. Outlook attachments arrive as base64 on contentBytes. PDF4me Merge multiple PDF files needs binary buffers. The same base64ToBinary(...) call bridges both sources, just pointed at different property names. Skip the conversion and the merge action errors with "invalid content".

Why a timestamped output name? Two emails from the same sender within an hour would collide if the filename was constant. concat('merge-', formatDateTime(utcNow(), 'yyyyMMdd-HHmmss'), '.pdf') produces names like merge-20260611-103323.pdf, lexicographically sortable, second-precision unique, and instantly readable as a UTC timestamp.


What You'll Get

Input: Any incoming email at the watched address (here, [email protected]) with one or more attachments. This walkthrough uses a short message body ("Hi, Hope you are doing well...") and two DOCX attachments (3Page.docx, Docx_2Pages.docx). Output: A single consolidated PDF dropped into /blog data/merge blog template/output/, named merge-YYYYMMDD-HHmmss.pdf. Page 1 is the body. Page 2 onwards is each attachment in attachment order, rendered through Aspose Words.

The incoming email

Gmail email view showing message addressed to test, body Hi Hope you are doing well I am writing to inform you of the progress of the current project Some related files are also attached for your reference Regards Test Dev, two attachments shown as Word document thumbnails 3Page.docx and Docx_2Pages.docx, Scanned by Gmail notice visible

The flow watches the Outlook inbox at [email protected]. Once Gmail forwards or this address receives directly, the trigger fires.

The output Dropbox folder after two runs

Dropbox output folder listing merge-20260611-103323.pdf 165.43 KB and merge-20260611-102949.pdf 171.37 KB, each named after the UTC timestamp the flow ran at

Page 1 of the merged PDF

merge-20260611-103323.pdf preview showing two pages side by side, left page is the converted email body Hi Hope you are doing well I am writing to inform you of the progress of the current project Some related files are also attached for your reference Regards Test Dev, right page is the first page of 3Page.docx labelled Page 1

Page 1 is the converted email body. Page 2 onwards is the first attachment (3Page.docx). The second attachment (Docx_2Pages.docx) follows after that. One PDF, full message reconstructed.


What You Need

  • Power Automate. Open Power Automate. Any plan with premium connectors (PDF4me Connect is premium).
  • PDF4me API key. Get your API key. Connect it the first time you add a PDF4me action.
  • Office 365 Outlook. For the When a new email arrives (V3) trigger and to read attachment contentBytes. Any inbox that the connector can authenticate against works.
  • Dropbox (or SharePoint, OneDrive, Google Drive). For the final consolidated PDF.
  • An email with attachments. The walkthrough uses two DOCX samples. 3Page.docx and Docx_2Pages.docx. Email them to the watched address to mirror every screenshot.

Grab the samples and the expected output first. Download both DOCX files, attach them to a quick email to your watched address, then publish the flow. Your first run should produce a merge-YYYYMMDD-HHmmss.pdf that mirrors the linked sample exactly (only the timestamp differs).


The Flow at a Glance

  1. Outlook: When a new email arrives (V3) (trigger).
  2. Initialize variable named email (Array).
  3. PDF4me: Convert to PDF (Body). Reads the HTML body, returns a PDF.
  4. Append to array variable with base64ToBinary on the converted body.
  5. Apply to each on triggerOutputs()?['body/attachments']:
    • PDF4me: Convert to PDF the attachment.
    • Append to array variable with base64ToBinary on the converted PDF.
  6. PDF4me: Merge multiple PDF files into a single PDF file. Array in, one PDF out.
  7. Dropbox: Create file. File Name expression stamps the UTC timestamp.

Complete flow overview

Power Automate flow canvas showing seven actions stacked top to bottom: When a new email arrives V3, Initialize variable, Convert to PDF Body, Append to array variable (email body), Apply to each containing Convert to PDF and Append to array variable, Merge multiple PDF files into a single PDF file, Create file

Seven actions. The Apply to each card shows "1 of 2" because the test email had two attachments. PDF4me sits in the flow three times: convert body, convert attachment, merge everything.


Step 1: Outlook: When a New Email Arrives (V3)

Flow so far: trigger only.

  1. In Power Automate click Create, choose Automated cloud flow.
  2. Name the flow (e.g. Email to Consolidated PDF), pick Office 365 Outlook: When a new email arrives (V3) as the trigger, click Create.
  3. Connect your Office 365 mailbox.
  4. Expand Advanced parameters and Show all to reveal filtering options.
  5. Configure:
    • To: [email protected] (filter so only mail addressed to this address triggers the flow)
    • Include Attachments: Yes
    • Only with Attachments: Yes

Trigger configuration

Power Automate Office 365 Outlook When a new email arrives V3 trigger Advanced parameters expanded showing To set to test@ynoox.ch, Include Attachments Yes, Only with Attachments Yes, Connected to Office 365 Outlook

Tip. Only with Attachments Yes is what keeps the flow from firing on every internal notification, calendar invite, or reply. Combine with a To filter on a dedicated alias (e.g. [email protected]) for a clean intake address that only receives mail you actually want consolidated.


Step 2: Initialize Variable email

Flow so far: trigger plus Initialize variable.

Open an empty array to collect the body PDF and every attachment PDF.

  1. Click + New step, pick Initialize variable.
  2. Configure:
    • Name: email
    • Type: Array
    • Value: leave blank

Initialize variable configuration

Power Automate Initialize variable action configured with Name email and Type Array and an empty Value field

Step 3: PDF4me: Convert to PDF (Body)

Flow so far: trigger plus Initialize variable plus Convert to PDF Body.

The Outlook trigger emits the message body as HTML on body/body. PDF4me Convert to PDF reads HTML directly, so the entire conversion is one action.

  1. Click + New step, search PDF4me, pick Convert to PDF.
  2. Rename the action to Convert to PDF Body so step 5's Convert to PDF action can have its own clean name later.
  3. Connect PDF4me Connect with your API key (first time only).
  4. Configure:
    • File Content (expression, click fx): triggerOutputs()?['body/body']
    • File Name: email-body.html

Convert to PDF Body configuration

Power Automate PDF4me Convert to PDF Body action with File Content set to the trigger Body token (expression triggerOutputs body/body) and File Name email-body.html, Connected to PDF4me Connect

The .html extension on the filename tells PDF4me which renderer to use. Send .docx, .xlsx, .png, or .jpg just as easily, the same action handles all of them.


Step 4: Append to Array Variable (Email Body)

Flow so far: trigger plus Initialize variable plus Convert to PDF Body plus Append (email body).

Push the converted body PDF onto the email array as page 1.

  1. Click + New step, pick Append to array variable.
  2. Rename the action to Append to array variable (email body) for clarity.
  3. Configure:
    • Name: email
    • Value (expression): base64ToBinary(body('Convert_to_PDF_Body')?['$content'])

Append email body configuration

Power Automate Append to array variable (email body) action with Name email and Value set to the expression base64ToBinary body open quote Convert_to_PDF_Body close quote close paren question dollar content close bracket shown in the expression editor

Step 5: Apply to Each Attachment, Convert, and Append

Flow so far: trigger plus Initialize variable plus Convert to PDF Body plus Append (email body) plus Apply to each.

Loop over every attachment, convert each to PDF, and append in turn so the final merge respects attachment order.

Step 5a: Apply to each (pick the source)

  1. Click + New step, pick Apply to each.
  2. Configure:
    • Select an output from previous steps (expression): triggerOutputs()?['body/attachments']
Power Automate Apply to each action with Select an output from previous steps set to the Attachments token, resolved expression triggerOutputs body/attachments shown in tooltip

Step 5b: Convert to PDF (the attachment)

  1. Inside the loop click Add an action, pick PDF4me: Convert to PDF.
  2. Configure:
    • File Content (expression): base64ToBinary(items('Apply_to_each')?['contentBytes'])
    • File Name (dynamic content): pick name from the Apply to each current item
Power Automate Convert to PDF action inside Apply to each, File Content set to base64ToBinary items Apply_to_each contentBytes, File Name set to the name token from the current attachment, expression editor open showing base64ToBinary items Apply_to_each contentBytes

The name dynamic-content token resolves at run time to items('Apply_to_each')?['name'], so each attachment is converted using its original filename (which carries the source extension and tells PDF4me which renderer to use).

Power Automate Convert to PDF File Name field tooltip showing the resolved expression items Apply_to_each name, name token from Apply to each current attachment

Step 5c: Append to array variable (the converted attachment)

  1. Below Convert to PDF inside the same loop, Add an action, pick Append to array variable.
  2. Configure:
    • Name: email
    • Value (expression): base64ToBinary(body('Convert_to_PDF')?['$content'])
Power Automate Append to array variable inside Apply to each, Name email, Value set to base64ToBinary body Convert_to_PDF dollar content shown in the expression editor

The two property names matter. Outlook attachment bytes live on contentBytes. PDF4me Convert to PDF output lives on $content. Both are base64. Keep the names straight when you paste the expressions and the loop just works.


Step 6: PDF4me: Merge Multiple PDF Files

Flow so far: trigger plus Initialize variable plus Convert to PDF Body plus Append (body) plus Apply to each plus Merge multiple PDFs.

The whole email array (body PDF first, then attachment PDFs in attachment order) goes into a single Merge call.

  1. Below the Apply to each loop click + New step, search PDF4me, pick Merge multiple PDF files into a single PDF file.
  2. Configure:
    • Body/docContent: pick the email variable token
    • Output File Name: Email Attachment Merge Output.pdf

Merge action configuration

Power Automate PDF4me Merge multiple PDF files into a single PDF file action with Body/docContent set to the email variable and Output File Name Email Attachment Merge Output.pdf, dynamic content panel open showing Variables email, Apply to each, Current item, When a new email arrives V3, body/sensitivityLabelInfo, Attachments

Step 7: Dropbox: Create File with a Timestamped Name

Flow so far: trigger plus Initialize variable plus Convert to PDF Body plus Append plus Apply to each plus Merge plus Create file.

Write the consolidated PDF to Dropbox with a UTC-timestamped filename so every run lands as a fresh, sortable file.

  1. Click + New step, pick Dropbox: Create file.
  2. Configure:
    • Folder Path: /blog data/merge blog template/output
    • File Name (expression, click fx): concat('merge-', formatDateTime(utcNow(), 'yyyyMMdd-HHmmss'), '.pdf')
    • File Content: pick the File Content token from the Merge multiple PDFs action

File name expression

Power Automate Dropbox Create file action Folder Path /blog data/merge blog template/output, File Name expression concat merge dash comma formatDateTime utcNow comma yyyyMMdd dash HHmmss comma dot pdf shown in the expression editor, File Content set to Merge multiple token

Final action with File Content mapped

Power Automate Dropbox Create file action with Folder Path /blog data/merge blog template/output, File Name concat expression, File Content token from Merge multiple PDF files into a single PDF file action, tooltip resolving to body Merge_multiple_PDF_files_into_a_single_PDF_file

Tip. Want one folder per sender domain? Replace the static Folder Path with concat('/inbox-pdfs/', split(triggerOutputs()?['body/from'], '@')[1], '/'). PDFs auto-route to subfolders like /inbox-pdfs/yourcompany.com/ based on the sender's domain.


Run the Flow and Verify

  1. Save the flow at the top right.
  2. Email a message with one or more attachments to the watched address (the walkthrough used [email protected]).
  3. Open Run history. The trigger should fire within seconds. Watch each action turn green: Convert to PDF Body takes about 10 seconds for a short HTML body, Apply to each runs in parallel by default and finishes both attachments in about 22 seconds, Merge multiple PDFs takes around 11 seconds.
  4. Open Dropbox at /blog data/merge blog template/output/. A file named merge-YYYYMMDD-HHmmss.pdf is there.
  5. Open the PDF. Page 1 is the body text. Page 2 onwards is each attachment, fully rendered, in attachment order.

What did you actually build? A zero-touch email-to-archive pipeline. Every message with attachments turns into one consolidated PDF, body first then attachments in order, timestamped so nothing collides, dropped into a shared folder your team or auditors can search. The same recipe handles two attachments or twenty without any change. Same recipe works in Make (Outlook trigger plus iterator over attachments plus PDF4me Merge) and Zapier (Email by Zapier or Outlook trigger plus Looping plus PDF4me Merge).


Common Variations You Can Add Without Rebuilding

Filter attachments by type
If you only want PDFs and Office docs in the merge, wrap Convert to PDF in a Condition testing endsWith(toLower(items('Apply_to_each')?['name']), '.pdf') or similar. Skips signatures and inline image attachments cleanly.
Stamp metadata onto page 1
Before step 3, build an HTML preamble with Compose: sender, subject, received date, then concatenate it to body/body. Page 1 of the consolidated PDF now carries a header you can grep on later.
Digital sign before archive
Insert PDF4me Digital Sign PDF between step 6 and step 7. The archived PDF is countersigned with your X.509 certificate, an instant audit trail of who archived what when.
PDF/A for long-term retention
Add PDF4me Create PDF/A with Compliance PDF/A-2b after the merge. The archived consolidated PDF is now ISO 19005 compliant, fit for legal hold or regulator submission.

Troubleshooting

Merge action errors with "invalid content"

One of the Append to array Value expressions is not wrapped in base64ToBinary(...). Each push (body and attachments) needs the conversion. Re-open both Append actions, switch to the expression editor, and confirm the expressions are base64ToBinary(body('Convert_to_PDF_Body')?['$content']) and base64ToBinary(body('Convert_to_PDF')?['$content']) verbatim.

Page 1 is blank or missing

The Convert to PDF Body action did not receive a real HTML body. Check the trigger's Include Attachments stayed Yes (without it the body sometimes arrives empty) and that File Content resolves to triggerOutputs()?['body/body'], not body/bodyPreview.

Apply to each runs zero times

The trigger fired on a mail with no attachments because Only with Attachments is set to No. Either flip it to Yes, or wrap the Apply to each in a Condition that runs only when length(triggerOutputs()?['body/attachments']) is greater than zero.

Attachments come out in wrong order

The default Apply to each runs in parallel, which can scramble append order. Open the loop's Settings and set Concurrency Control Degree of parallelism to 1. The loop now runs sequentially and attachments land in the array in the exact order Outlook returned them.


Next Steps

The same seven-step pattern (trigger then init array then convert body then loop-convert-and-append then merge then save with a timestamp) works for any "email plus attachments to one PDF" automation. Ship it once and every incoming message becomes a searchable, sortable archive entry, automatically.