Platform live Verified signup, API keys, templates, HTML renders, sync or async jobs, webhooks, billing, and render history are available today. Need product help or rollout support? Email support@getdocrender.com.
Developer quickstart

Get to a working PDF without the setup spiral.

This page covers the happy path: authenticate, send printable HTML or a published template, and receive the result synchronously, by polling, or through signed webhooks.

5 trial renders No card required x-api-key auth signed webhooks

Your first request

The minimum launch flow is simple: send your API key, pass HTML or a template reference, choose sync or async behavior, and return a finished file.

curl -X POST https://getdocrender.com/api/v1/render/pdf \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: invoice-100" \
  -d '{
    "mode": "sync",
    "allowAsyncFallback": true,
    "html": "<h1>Invoice</h1><p>Total due: GBP 100</p>",
    "options": {
      "fileName": "invoice-100.pdf",
      "page": {
        "size": "A4",
        "orientation": "portrait"
      }
    }
  }'

From demo studio to API-ready template

The dashboard now includes a live demo studio for the full template loop: paste HTML, add Handlebars variables, preview it with example JSON, then save or publish the same source as a reusable template.

Supported template logic stays intentionally small

Use variable interpolation plus {{#if}} and {{#each}}. Partials, subexpressions, and advanced helpers are rejected so the published template path stays predictable in production.

  1. 01
    Open the dashboard demo studio

    Go to /dashboard/demo, paste or upload your HTML, and keep iterating until the live preview looks right.

  2. 02
    Add example data for Handlebars preview

    When example JSON is present, the studio renders the template path instead of only showing raw HTML, which lets you validate loops, conditionals, and variable names before publish.

  3. 03
    Save draft or publish v1

    Publishing validates the markup and stored example data so the current version is ready for API renders immediately after save.

  4. 04
    Run the published template through the API

    The template detail screen shows a ready-to-run request shape using the saved template id, pinned version id, and example data.

<section class="invoice">
  <h1>{{documentTitle}}</h1>
  <p>Prepared for {{customer.name}}</p>
  <ul>
    {{#each items}}
      <li>{{label}} - {{../currency}} {{amount}}</li>
    {{/each}}
  </ul>
  {{#if notes}}
    <p>{{notes}}</p>
  {{/if}}
</section>
curl -X POST https://getdocrender.com/api/v1/render/pdf \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: template-demo-001" \
  -d '{
    "mode": "sync",
    "allowAsyncFallback": true,
    "templateId": "tmpl_your_template_id",
    "templateVersionId": "tmv_your_published_version_id",
    "data": {
      "documentTitle": "April invoice pack",
      "customer": {
        "name": "Northline Studio"
      },
      "currency": "GBP",
      "items": [
        {
          "label": "Design system sprint",
          "amount": "1600.00"
        }
      ],
      "notes": "Approved for release."
    },
    "options": {
      "fileName": "invoice-pack.pdf"
    }
  }'

Stack starters

Start with the server runtime you already ship. Every example below sends the same render payload, keeps the API key private, and branches between a finished PDF response and a queued job.

Secret

Load DOCRENDER_API_KEY from your server environment and send it as x-api-key.

Request

Send JSON to POST /api/v1/render/pdf with printable HTML or a published template.

Response

Handle application/pdf for completed sync renders and 202 for queued async jobs.

Frontend apps use a server hop

If your product UI is React, call DocRender from your own backend or a Next.js route handler instead of the browser. That keeps x-api-key private and gives your UI one place to handle queued jobs.

Before you paste a snippet

These setup steps are the same in every language.

  1. 01
    Store the API key server-side

    Use DOCRENDER_API_KEY in local development and production so the examples drop in without rewrites.

  2. 02
    Pick a stable idempotency key

    Reuse a document-specific value such as invoice-100 so retries do not create duplicate work.

  3. 03
    Decide what your app does with queued jobs

    Start by logging the job id, then add polling or webhooks once the document flow becomes part of your product UI.

Python

requests

Good for Django, Flask, FastAPI, and worker processes that need to write the PDF immediately or keep the queued job id.

  • Install pip install requests
  • Secret export DOCRENDER_API_KEY=...
  • What you get Writes invoice-100.pdf when sync completes, otherwise prints the queued job id.
import os
import requests

payload = {
    "mode": "sync",
    "allowAsyncFallback": True,
    "html": "<h1>Invoice</h1><p>Total due: GBP 100</p>",
    "options": {
        "fileName": "invoice-100.pdf",
        "page": {"size": "A4", "orientation": "portrait"},
    },
}

response = requests.post(
    "https://getdocrender.com/api/v1/render/pdf",
    headers={
        "x-api-key": os.environ["DOCRENDER_API_KEY"],
        "Idempotency-Key": "invoice-100",
    },
    json=payload,
    timeout=30,
)

if response.status_code == 200 and response.headers.get("content-type") == "application/pdf":
    with open("invoice-100.pdf", "wb") as file:
        file.write(response.content)
elif response.status_code == 202:
    job = response.json()["data"]["job"]
    print(f"Render queued: {job['id']}")
else:
    raise RuntimeError(response.text)
.NET

HttpClient

Fits ASP.NET APIs, background services, and console jobs that need the same sync-versus-queued branch in managed code.

  • Package Uses the built-in HttpClient and System.Text.Json stack in modern .NET.
  • Secret DOCRENDER_API_KEY in your local secret store or hosting environment
  • What you get Copies the PDF stream to disk or logs the queued job id so you can add polling next.
using System.Net;
using System.Text;
using System.Text.Json;

var client = new HttpClient();
var payload = new
{
    mode = "sync",
    allowAsyncFallback = true,
    html = "<h1>Invoice</h1><p>Total due: GBP 100</p>",
    options = new
    {
        fileName = "invoice-100.pdf",
        page = new { size = "A4", orientation = "portrait" }
    }
};

var request = new HttpRequestMessage(
    HttpMethod.Post,
    "https://getdocrender.com/api/v1/render/pdf"
);
request.Headers.Add("x-api-key", Environment.GetEnvironmentVariable("DOCRENDER_API_KEY"));
request.Headers.Add("Idempotency-Key", "invoice-100");
request.Content = new StringContent(
    JsonSerializer.Serialize(payload),
    Encoding.UTF8,
    "application/json"
);

var response = await client.SendAsync(request);

if (response.Content.Headers.ContentType?.MediaType == "application/pdf")
{
    await using var input = await response.Content.ReadAsStreamAsync();
    await using var output = File.Create("invoice-100.pdf");
    await input.CopyToAsync(output);
}
else if (response.StatusCode == HttpStatusCode.Accepted)
{
    using var body = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
    var jobId = body.RootElement.GetProperty("data").GetProperty("job").GetProperty("id").GetString();
    Console.WriteLine($"Render queued: {jobId}");
}
else
{
    throw new Exception(await response.Content.ReadAsStringAsync());
}
TypeScript

Next.js route handler

Best when your frontend is React and you want one server-side endpoint that keeps the API key private and forwards the result to the browser.

  • File app/api/render-pdf/route.ts
  • Secret DOCRENDER_API_KEY in .env.local or your deployment environment
  • What you get Returns PDF bytes directly to the client or relays the queued job JSON using the upstream status code.
export async function POST() {
  const upstream = await fetch("https://getdocrender.com/api/v1/render/pdf", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-api-key": process.env.DOCRENDER_API_KEY!,
      "Idempotency-Key": "invoice-100",
    },
    body: JSON.stringify({
      mode: "sync",
      allowAsyncFallback: true,
      html: "<h1>Invoice</h1><p>Total due: GBP 100</p>",
      options: {
        fileName: "invoice-100.pdf",
        page: { size: "A4", orientation: "portrait" },
      },
    }),
  });

  if (upstream.headers.get("content-type")?.includes("application/pdf")) {
    return new Response(await upstream.arrayBuffer(), {
      status: 200,
      headers: {
        "Content-Type": "application/pdf",
        "Content-Disposition": 'attachment; filename="invoice-100.pdf"',
      },
    });
  }

  return Response.json(await upstream.json(), { status: upstream.status });
}

Request shape

Most teams only need a small set of fields to start. Keep the payload explicit and add more detail only when the document flow needs it.

  • html or templateId Exactly one source is required. Use HTML for direct control or a published template for repeatable documents.
  • mode and allowAsyncFallback Choose the sync-first path and let the platform fall back to async when a render cannot finish inside the inline budget.
  • data and templateVersionId Template renders accept structured JSON data and can pin an immutable published version for reproducibility.
  • options.page Control document dimensions for standard sizes such as A4 or letter and pick portrait or landscape per render.
  • options.fileName and metadata Keep downstream storage, user downloads, and job context consistent without bloating the request shape.

Async and webhooks

Async rendering is part of the core platform. Poll jobs through the API or configure dashboard-managed webhook endpoints to receive signed completion and failure events automatically.

Poll accepted jobs

When the render returns 202, keep the job id and poll the status endpoint until artifact.available becomes true. The completed payload includes a signed artifact.downloadUrl until the file expires.

Verify webhook signatures

Webhook deliveries include X-DocRender-Event, X-DocRender-Event-Id, and X-DocRender-Signature. Verify the raw JSON body with your endpoint secret before you trust the event payload.

curl https://getdocrender.com/api/v1/render/jobs/rj_01JH8PX3RX4S7M1D8P4R5WQY2V \
  -H "x-api-key: YOUR_API_KEY"

Platform behavior

The point of the API is not just to produce a PDF. It is to produce one your product can trust when the document is long, branded, and business critical.

Sync first, async when needed

The platform defaults to sync rendering, then falls back to async job handling when a document cannot complete within the configured wait budget.

Document controls

Use print CSS, page-level settings, headers, footers, and file naming to keep business documents readable and predictable.

Polling and history

Every accepted render creates a job record, which means the dashboard and the job status endpoint can both explain what happened next.

Artifact retention

Completed files stay available through signed download URLs during the retention window, while job metadata remains visible for support and debugging.

API reference

When you need the exact contract rather than the shortest path, start here. The render API accepts either HTML or a published template, returns PDF bytes when sync completes in budget, and returns a durable job envelope when the request resolves asynchronously.

Endpoint

POST /api/v1/render/pdf creates the render job and returns either a finished PDF or a job response you can poll.

Status endpoint

GET /api/v1/render/jobs/:id returns the current job state, artifact availability, billing context, and any render error details.

Render request

The core request accepts either HTML or a published template plus a few layout settings. Keep the launch path small, then add metadata or template data only when the workflow truly needs them.

{
  "mode": "sync",
  "allowAsyncFallback": true,
  "templateId": "tmpl_invoice_default",
  "data": {
    "customer": { "name": "River & Co" }
  },
  "options": {
    "fileName": "invoice-100.pdf",
    "page": {
      "size": "A4",
      "orientation": "portrait"
    }
  },
  "metadata": {
    "documentType": "invoice",
    "accountId": "acct_123"
  }
}

Headers and fields

Treat the request as a compact document contract: authentication in headers, layout controls in the JSON body, and only the metadata your workflow truly needs.

Required request details

Send the x-api-key header, declare application/json, and include either printable HTML or a template reference plus the sync or async behavior you want.

Core contract

DocRender supports HTML renders, template renders, job polling, dashboard-managed webhook delivery, and billing-aware trial exhaustion messaging.

  • x-api-key Authenticates server-side access to your account and meters document generation.
  • html or templateId Exactly one source is required for every render request.
  • data and templateVersionId Template renders accept structured JSON data and can pin an immutable published version.
  • mode and allowAsyncFallback Control whether the request waits for inline completion or returns an async job when the render cannot finish inside the sync budget.
  • options and metadata Define file naming, page layout, and downstream job context without bloating the top-level request contract.

Response shape

Successful sync renders return PDF bytes with metadata headers. Async acceptance returns a job envelope that you can poll until the artifact is ready or your webhook endpoint receives a signed event.

{
  "data": {
    "job": {
      "id": "rj_01JH8PX3RX4S7M1D8P4R5WQY2V",
      "status": "queued",
      "requestedMode": "sync",
      "actualMode": "async",
      "artifact": {
        "available": false,
        "downloadUrl": null,
        "expiresAt": null
      }
    }
  }
}

Operational notes

The integration work usually lives around validation, failure handling, and how long the finished file stays available in your own product flow.

401 Authentication error

The API key is missing or invalid, so the render request should not proceed.

400 Invalid request

The request envelope is missing required fields or uses an invalid value for mode, page settings, or metadata.

409 Async required

When allowAsyncFallback is false and the render cannot finish within the sync budget, the API returns RENDER_REQUIRES_ASYNC.

422 or 5xx Render failure

The markup may be invalid for printing, or the platform may have hit a transient issue. Use idempotency and the job record to retry safely.

Next steps

Create an account if you have not already, then start with the quickstart flow and keep the API reference on the same page when you need the exact request shape, polling contract, webhook behavior, and failure modes.