HTML to PDF in Power Automate

Power Automate has no "HTML to PDF" action. The usual workaround saves an .html file to OneDrive and runs Convert file on it, which converts through Office rather than a browser. The direct way is one HTTP action: POST your HTML to a PDF API as the raw request body, and the response is the PDF file, ready for OneDrive, SharePoint or an Outlook attachment. No JSON to build, so quotes, apostrophes and line breaks in your data can't break the request.

What was tested (October 2026): the exact request the HTTP action below sends (POST, the two headers, the HTML as the body) was sent to the live API with an HTML table like the one Create HTML table outputs, including ", ', &, € and 東京 in the data; the PDFs were checked with pdfinfo and rendered to images. We did not run a flow ourselves (that needs a Microsoft account), so check the field names below against your designer. HTTP is a premium connector: it needs a Power Automate Premium (or per-flow) licence.

1. Get a free API key (once)

Click Get a free key and copy it: 25 free renders a month, no signup. One PDF is one credit, and failed renders are free.

2. Build the HTML in a Compose action

Add a Data Operation → Compose action named HTML and type your page into Inputs, inserting dynamic content where it belongs. Put the styles in a <style> tag, since the PDF is printed by Chromium like a web page:

<html><head><meta charset="utf-8"><style>
  body { font-family: sans-serif; margin: 0; color: #222 }
  h1 { font-size: 22px; margin: 0 0 4px }
  table { width: 100%; border-collapse: collapse; margin-top: 16px }
  th { background: #f1f3f5; text-align: left }
  th, td { border-bottom: 1px solid #ddd; padding: 8px }
</style></head><body>
<h1>Open orders</h1>
<p>Generated @{formatDateTime(utcNow(), 'yyyy-MM-dd')}</p>
@{body('Create_HTML_table')}
</body></html>

Rows from Excel, SharePoint or Dataverse: put a Data Operation → Create HTML table action before Compose, with From set to the value of your List rows present in a table or Get items step. It outputs a bare <table> with no styling, which is why the CSS above styles table, th and td directly. For an invoice, write the table rows yourself instead and insert the fields. Images need absolute https:// URLs (a SharePoint image behind your login won't load; use a public URL or embed it as a data: URI).

3. The HTTP action

Add HTTP (premium) and fill in:

FieldValue
MethodPOST
URIhttps://shotmint.creasework.com/v1/pdf?format=A4&margin=15mm
other options go here too, see below
HeadersX-API-Key: your key
Content-Type: text/html; charset=utf-8
Bodythe Outputs of the HTML Compose action

That is the whole call. Because the body is the HTML itself (not JSON with the HTML inside it), there is nothing to escape. In code view (… → Peek code) the action looks like this:

{
  "type": "Http",
  "inputs": {
    "method": "POST",
    "uri": "https://shotmint.creasework.com/v1/pdf?format=A4&margin=15mm",
    "headers": {
      "X-API-Key": "YOUR_KEY",
      "Content-Type": "text/html; charset=utf-8"
    },
    "body": "@outputs('HTML')"
  }
}

On our tests the call took 0.11-0.13 s for a one-page table and returned a 21 KB A4 PDF with selectable text. Turn on Settings → Secure inputs for the HTTP action so the key isn't shown in the run history, or keep it in an environment variable.

Options (in the URI)

Prefer JSON? A body of {"html": "...", "format": "A4"} with Content-Type: application/json works too, and so do form fields; the raw HTML body is just the shortest setup.

4. Save or send the PDF

The HTTP action's Body output is the PDF file (Power Automate keeps it as binary content, because the response's Content-Type is application/pdf):

If a target ever stores a file that won't open, pass base64ToBinary(body('HTTP')?['$content']) instead, which is the same bytes unwrapped.

Errors and credits

The HTTP action fails the flow on any 4xx or 5xx, and its output holds the API's JSON with the reason. To handle it (e.g. mail yourself), add an action after HTTP and set Configure run after to has failed.

StatusMeaning and fix
401Missing or wrong key
check the X-API-Key header name and value
400Bad request, e.g. Provide url (or html) when the body is empty
check that Body points at the Compose output
402Out of credits
the error names a top-up link for the same key; free keys refill to 25 on the 1st

The response headers carry X-Credits-Remaining, so a flow can warn you before a key runs dry. Paid credits never expire: pricing.

Screenshots too

The same HTTP action takes a website screenshot: Method GET, URI https://shotmint.creasework.com/v1/screenshot?url=@{encodeUriComponent('https://example.com')}&full_page=true, header X-API-Key, and the Body is a PNG for OneDrive, SharePoint or Teams. Or POST HTML to /v1/screenshot with Content-Type: text/html to turn a card or chart into an image. Using Zapier, Make or n8n instead? See Zapier, Make and n8n.

Try it with 25 free renders a month

No signup, no card. Get a key in one click, then buy credits only if you need more (from €5 for 2,000, never expire). From a script, an AI agent, Make or Zapier, POST /v1/keys/free (no body) returns a key as JSON (openapi.json, no-code recipes).

Get a free API key Full API reference

More guides: No-code and CI integrations

All guides: guides · integrations · comparisons