We are in official beta.

Webhooks & Integrations

Trigger entire DocButterfly document pipelines with a single HTTPS request — from Power Automate, Logic Apps, Zapier, n8n, a cron job, a script, or any system that can make an HTTP call.

What Are DocButterfly Webhooks?

There are two ways to use the DocFlow API:

  • Direct endpoint calls — POST to any of our 250 endpoints (e.g. /api/ConvertHtmlToPdf) and get a result back. One operation per call. See the API Reference.
  • Webhooks (published orchestrations) — build a multi-step pipeline visually in the Orchestrator, publish it, and get a unique URL. One HTTPS POST runs the whole pipeline server-side and returns the final result.

Webhooks are the fastest way to integrate: your calling system doesn't need to chain calls, handle intermediate files, or know anything about our API beyond one URL, one header, one JSON body.

If it can make an HTTPS request, it can use DocButterfly. Power Automate, Logic Apps, Zapier, Make, n8n, UiPath, GitHub Actions, cURL, PowerShell, Python, a Raspberry Pi — anything.

How It Works

  1. Build — assemble a pipeline in the Orchestrator (e.g. HTML → PDF → watermark → email). Each step's output feeds the next.
  2. Publish — click Publish. You get a unique webhook URL like /api/Webhook/invoice-pipeline-3f9a1c.
  3. Call — POST to that URL with your API key. The JSON body you send becomes $input, available to every step in the pipeline.
  4. Receive — the response contains the final step's output plus execution metadata (steps run, tokens used, duration).

The whole pipeline is billed as one token charge (the sum of the step costs), checked up front and deducted only on success. Failed runs are never billed.

Prerequisites

  • A DocButterfly account — free during beta, 500 tokens every month.
  • Your API key, from Portal → API Key. Keys look like df_... and are shown once at creation — store them in a secret manager (Azure Key Vault, environment variable, Zapier/Make connection field), never in source code.
  • A published orchestration (for webhook calls) — or just pick an endpoint from the API Reference for direct calls.

Base URL

https://api.docbutterfly.com

All paths on this page are relative to this base URL. Your full webhook URL (including its unique slug) is shown in the Orchestrator's webhook bar after publishing — use the copy button there.

Register the URL exactly as the Orchestrator shows it. docbutterfly.com is this website and serves no API, so a webhook pointed there is delivered to a 404 — and most senders retry quietly before they give up. See Base URL & hosts.

Step 1 — Build and Publish an Orchestration

  1. Open Portal → Orchestrator and drag actions from the palette onto the spine, or click a + between two steps and pick one. Dropping a step onto the card above it binds that card's output to the new step's input. Full guide.
  2. Set a parameter to $input (or $input.fieldName) wherever you want data supplied by the caller at run time — for example, the HTML for a PDF, a recipient email address, or a watermark label.
  3. Save the workflow, then click Publish. A webhook URL appears in the yellow bar at the top:
    Your webhook URL
    https://api.docbutterfly.com/api/Webhook/invoice-pipeline-3f9a1c

The slug (invoice-pipeline-3f9a1c) is unique to your workflow and stays stable across re-publishes, so callers never need updating when you edit the pipeline. Unpublishing deactivates the URL immediately (callers get 404).

Step 2 — Call the Webhook

One POST request. Three things matter:

PartValue
Method / URLPOST /api/Webhook/{your-slug}
Auth headerX-API-Key: df_your_api_key  (or Authorization: Bearer df_...)
Body (optional)Any JSON object — exposed to the pipeline as $input. Omit it if your pipeline takes no input.
Request
HTTP
POST /api/Webhook/invoice-pipeline-3f9a1c HTTP/1.1
Host: api.docbutterfly.com
X-API-Key: df_your_api_key
Content-Type: application/json

{
  "html": "<h1>Invoice #1042</h1><p>Amount due: $250.00</p>",
  "customerEmail": "billing@contoso.com",
  "watermarkText": "PAID"
}
Response
200 OK — application/json
{
  "success": true,
  "pipeline": "Invoice Pipeline",
  "steps": 3,
  "totalTokens": 3,
  "duration": 4182,
  "result": {
    "data": "JVBERi0xLjcKJeLjz9...",   // base64 output of the final step
    "contentType": "application/pdf"
  }
}

result is whatever the last step of your pipeline produces. For file-producing steps that's base64 data plus a content type (decode it on your side — examples below). For data steps (text extraction, AI extraction, etc.) it's that step's JSON.

Every successful response also includes billing headers:

  • X-Tokens-Used — tokens charged for the whole pipeline run
  • X-Tokens-Remaining — your balance after this run

Things that call it for you

You do not have to be the one making the request. A published workflow can also be started by a schedule, by a web form we host, or by a SharePoint watcher. All three call the same URL you just published, so everything on this page — the body shape, the response, the billing, the failure codes — is unchanged. Attach them from the intake at the top of the workflow in the Orchestrator; there is more on each in the builder guide.

Schedules

Type a cadence on the intake and the workflow runs on a clock, unattended. Plain words are accepted, and so is a five-field cron expression:

Cadences the intake accepts
every 15 minutes
every 2 hours
hourly
daily at 06:00
weekly on Monday at 06:00
monthly on the 1st at 06:00
0 6 * * *                    a cron line, if you would rather
Every time here is UTC — the cadence you type, the cron line, and the "next run" the intake shows. Nothing adjusts for your time zone or for daylight saving, because a schedule that silently moved by an hour twice a year would be worse than one that never moves.
  • What it sends. The schedule POSTs the published URL for you, with the payload you gave it (an empty body is normal — a workflow whose intake is a query fetches its own input).
  • What it costs. Exactly what that call costs from anywhere else, on the same balance, with the same usage record. A schedule that exists but is not due costs nothing, and having one is free.
  • It does not catch up. After an outage, an every-five-minutes schedule runs once, not once per missed slot.
  • It stops itself when it is broken. Five consecutive failures suspend it and the reason is kept on the schedule; a workflow that is no longer published suspends it on the first try. Running out of tokens never suspends it — top up and the next slot runs.
  • Pause, resume, run now, delete are all on the same panel, and pausing takes effect immediately.

Web forms and SharePoint watchers

A web form posts each submission as the body, keyed by your own field names, so $input.customerName is the box labeled "Customer name". A SharePoint watcher polls a library and starts the workflow when a file lands. Both are attached from the same intake panel, and both show what they have delivered, with a replay for anything that failed.

Call It From Anywhere

The same request in common languages and tools:

curl -X POST "https://api.docbutterfly.com/api/Webhook/invoice-pipeline-3f9a1c" \
  -H "X-API-Key: $DOCBUTTERFLY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"html": "<h1>Invoice #1042</h1>", "watermarkText": "PAID"}'
$response = Invoke-RestMethod `
    -Method Post `
    -Uri "https://api.docbutterfly.com/api/Webhook/invoice-pipeline-3f9a1c" `
    -Headers @{ "X-API-Key" = $env:DOCBUTTERFLY_API_KEY } `
    -ContentType "application/json" `
    -Body (@{ html = "<h1>Invoice #1042</h1>"; watermarkText = "PAID" } | ConvertTo-Json)

# Save the resulting PDF
[IO.File]::WriteAllBytes("invoice.pdf", [Convert]::FromBase64String($response.result.data))
import os, base64, requests

resp = requests.post(
    "https://api.docbutterfly.com/api/Webhook/invoice-pipeline-3f9a1c",
    headers={"X-API-Key": os.environ["DOCBUTTERFLY_API_KEY"]},
    json={"html": "<h1>Invoice #1042</h1>", "watermarkText": "PAID"},
)
resp.raise_for_status()
body = resp.json()

with open("invoice.pdf", "wb") as f:
    f.write(base64.b64decode(body["result"]["data"]))

print(f"Tokens used: {resp.headers['X-Tokens-Used']}, remaining: {resp.headers['X-Tokens-Remaining']}")
const resp = await fetch(
  "https://api.docbutterfly.com/api/Webhook/invoice-pipeline-3f9a1c",
  {
    method: "POST",
    headers: {
      "X-API-Key": process.env.DOCBUTTERFLY_API_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ html: "<h1>Invoice #1042</h1>", watermarkText: "PAID" }),
  }
);
if (!resp.ok) throw new Error(`Webhook failed: ${resp.status}`);
const body = await resp.json();

// Node: save the PDF
await require("fs/promises").writeFile(
  "invoice.pdf",
  Buffer.from(body.result.data, "base64")
);
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-API-Key",
    Environment.GetEnvironmentVariable("DOCBUTTERFLY_API_KEY"));

var resp = await http.PostAsJsonAsync(
    "https://api.docbutterfly.com/api/Webhook/invoice-pipeline-3f9a1c",
    new { html = "<h1>Invoice #1042</h1>", watermarkText = "PAID" });

resp.EnsureSuccessStatusCode();
var body = await resp.Content.ReadFromJsonAsync<JsonElement>();

var pdfBytes = Convert.FromBase64String(
    body.GetProperty("result").GetProperty("data").GetString()!);
await File.WriteAllBytesAsync("invoice.pdf", pdfBytes);

Platform Recipes

Step-by-step setup for popular automation platforms. Almost all of them reduce to the same thing: an HTTP action pointed at your webhook URL. Power Automate and Logic Apps are the exception — they can import our custom connector and get every action as a native, typed step instead.

Power Automate

Start with the custom connector. Every DocButterfly action — and every workflow you publish — imports as a native, typed step, with the API key held on the connection instead of pasted into each action. Power Automate & Logic Apps connector has the download and the five-minute import.

The HTTP recipe below is the fallback: use it for a platform that cannot import a connector, or for an endpoint the connector does not carry — everything stays reachable this way, with the same key.

  1. Add an HTTP action (premium connector) to your flow.
  2. Configure it:
    MethodPOST
    URIyour webhook URL
    HeadersX-API-Key : your key  •  Content-Type : application/json
    BodyJSON using dynamic content, e.g. { "html": ..., "customerEmail": ... }
  3. To save a returned file to SharePoint/OneDrive, add a Create file action with file content:
    Power Automate expression
    base64ToBinary(body('HTTP')?['result']?['data'])

Tip: store the API key in an environment variable or Azure Key Vault reference rather than pasting it into the flow definition.

Azure Logic Apps

The same custom connector imports into Logic Apps (Consumption and Standard) — verified end to end, every operation accepted unmodified. Import it once and DocButterfly actions appear under the Custom tab: import into Logic Apps.

Without it, the HTTP recipe is identical to Power Automate: add an HTTP action with method POST, your webhook URI, the X-API-Key header, and a JSON body. Use base64ToBinary() on result.data for downstream file connectors. In Logic Apps Standard, keep the key in app settings and reference it with parameters().

Zapier

  1. Add a Webhooks by Zapier action → Custom Request.
  2. Method POST, URL = your webhook URL, Data = your JSON, Headers: X-API-Key and Content-Type: application/json.
  3. Downstream steps can read result data (base64) and metadata fields from the parsed response.

Make (Integromat)

Add an HTTP → Make a request module: method POST, your webhook URL, header X-API-Key, body type Raw / JSON. Enable Parse response to map result.data into later modules (e.g. a Tools → Compose + base64 decode, or straight into an email attachment module that accepts base64).

n8n

Add an HTTP Request node: method POST, your webhook URL, authentication Generic → Header Auth (name X-API-Key), and JSON body. Follow with a Move Binary Data / Convert to File node using the base64 in result.data.

GitHub Actions / CI pipelines

YAML
- name: Generate release notes PDF
  run: |
    curl -sf -X POST "$WEBHOOK_URL" \
      -H "X-API-Key: ${{ secrets.DOCBUTTERFLY_API_KEY }}" \
      -H "Content-Type: application/json" \
      -d "{\"html\": \"<h1>Release ${{ github.ref_name }}</h1>\"}" \
      | jq -r '.result.data' | base64 -d > release-notes.pdf
  env:
    WEBHOOK_URL: https://api.docbutterfly.com/api/Webhook/your-slug

Everything else

UiPath, Workato, Tray, Pipedream, Salesforce Apex, SAP, a bash cron job, an ESP32 — if it can send an HTTPS POST with a header and a JSON body, it works. There is no SDK to install and no OAuth dance: one URL, one header, one body.

Single Operations Without a Webhook

If you only need one operation, skip the Orchestrator and call the endpoint directly — same authentication, same response headers:

cURL
curl -X POST "https://api.docbutterfly.com/api/ConvertHtmlToPdf" \
  -H "X-API-Key: $DOCBUTTERFLY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"html": "<h1>Hello</h1>", "returnBase64": true}'

Browse all endpoints in the API Reference, or try them live in the API Testbed. Use a webhook instead when you chain two or more operations — it saves round-trips, bandwidth (no shuttling intermediate files), and caller-side complexity.

Errors & Troubleshooting

StatusMeaningWhat to do
401Missing or invalid API keySend the key in X-API-Key; check for whitespace/truncation. Regenerate from Portal → API Key if compromised.
402Insufficient tokensThe response body includes required and available. Top up your balance.
403Account deactivatedContact support.
404Webhook not found or unpublishedCheck the slug; re-publish the workflow in the Orchestrator if you unpublished it.
500A pipeline step failedThe body includes message identifying the failing step. Failed runs are not billed. Test the pipeline in the Orchestrator, then retry.
Timeouts and cold starts

The first call after a quiet period can take 10–30 seconds extra while browser-based steps (HTML to PDF, web capture) warm up. Set your HTTP client timeout to at least 120 seconds for pipelines containing those steps, and add one retry on timeout in your caller.

Idempotency

Each call executes the pipeline and bills tokens once. If your platform retries automatically (Power Automate and Zapier both do on 5xx), be aware that a retry of a successful but slow call will run — and bill — the pipeline again. Prefer retrying only on network errors and 5xx responses.

Security Best Practices

  • Treat webhook URLs as semi-secret. The URL alone can't be used without an API key, but don't post it publicly.
  • Never embed API keys in client-side code (browser JavaScript, mobile apps). Call webhooks from a backend or automation platform.
  • Use HTTPS only (the API does not serve plain HTTP).
  • Rotate keys from Portal → API Key if a key may have leaked — the old key stops working immediately.
  • Unpublish unused webhooks. Unpublishing returns 404 to all callers instantly and is reversible.
  • Monitor usage in Portal → Usage — every call is logged with operation, tokens, caller IP, and duration.