The API that draws your documents

Your program describes the document it wants in JSON, posts it in one call, and the finished PDF comes back in the answer. This page says how, in plain words first, then for the developer writing that call, then key by key.

In plain words

Written for anybody. You can stop at the end of it and still know what this is and what it is for.

Think of ordering a cake. You write on a slip of paper what you want on it, hand the slip over the counter, and the cake comes back over the same counter a moment later. Nothing else happens, and you never see the kitchen.

The slip of paper here is a piece of text your program writes. It says what goes on the page: this sentence in this place, this table under it, this logo in the corner, this square code at the foot. Anything the page needs and the text cannot hold — a photograph, a sheet of headed paper — travels along beside it.

What comes back is the finished document, ready to send to a customer or to a printing works. It comes back in the same breath, not in an hour and not in a mailbox.

There are twenty-one things you may put on a page: paragraphs, tables, photographs, pages taken from another document, the five kinds of square and striped code, plain shapes, the boxes of a form to fill in, and clickable links. Each of them has a page of its own here, with the shortest example that draws it.

If you are reading this to decide whether it fits, the answer is on that list: everything you can ask for is written down, and nothing else is needed to start.

For the developer writing the call

Written for whoever writes the calling code: the transport, the model, the coordinate system and the failure modes.

The transport

One endpoint, one method: a POST with a multipart/form-data body. The part named json_data holds the whole description as JSON. Every other part is a named binary the description addresses by that part's name — a PDF used as a template or imported page by page, an image, an ICC profile, an XMP packet, the XML of an invoice. There is no upload step and no asset identifier to keep: a part lives for the call it travelled in.

Authentication is a bearer key checked by the reverse proxy, which resolves it to a client name and forwards that name in a header of its own. A success answers 200 with application/pdf, plus the parse time and the drawing time in milliseconds. A refusal answers a status code and one sentence of plain text. There is no envelope to unwrap and no job to poll.

What a call and its answer are made of
The call that draws a document POST /render
The call that says the service is up GET /health
The shape the body is sent in multipart/form-data
The part carrying the description json_data
Every other part a named binary the description addresses
How the key travels Authorization: Bearer <key>
What the proxy forwards to the renderer x-hqf-client: <name>
What a success carries 200 application/pdf
How long reading the description took x-hqf-parse-ms
How long the drawing took x-hqf-render-ms
What an error carries text/plain

The model

A request is a flat document: a list of items, and beside it the resources those items name. Fonts, colour spaces, gradients, graphics states, layers and drawings are each declared once under a name, and an item refers to that name. Nothing is positional and nothing is implicit — a name an item calls for and no declaration carries is refused, with the name in the message.

Pages are created by what is drawn on them, not declared up front. An item states which pages it lands on; the default, an empty array, is every page, which is how a footer or a watermark is written once. A table that overflows its box carries on into the next box and creates the pages it needs, and the items keyed to every page follow it there.

Geometry is in typographic points with the origin at the bottom left, as the format itself has it. Any coordinate may be a quoted arithmetic expression over the page dimensions and the current page number, so a right margin is "{page_width} - 56" rather than a number your program had to work out. An item may name itself and another may hang off its edge, which is what keeps a layout right when the height of a block is only known after it is set.

What a request carries beside the drawing

The same request states the document's metadata, the archival standard it claims, the output intent its colours are read against, the compression of its streams, the outline and the named destinations, the article threads, the page labels, the reading preferences a viewer honours, and the invoice XML a Factur-X document carries. One call produces a finished, standards-claiming file: there is no second pass and no post-processing tool.

Failure modes, and what they cost

Validation is total and happens before anything is drawn: the server declines a request rather than producing a document that quietly lost something. A refusal is one sentence naming the key or the name at fault, so it is worth logging verbatim.

Unknown keys are ignored, which makes a request written for a later version safe to send at an earlier one. A key written twice in the same object is declined by name. Combinations that would contradict each other — a matrix together with a rotation, a link with both a short target and a full action, a table cell holding two things — are declined by name as well.

Every answer the server gives, by its number
200 The PDF is the body, and the two timing headers stand beside it.
400 The description named something the drawing could not use: a font no declaration carries, a coordinate that will not parse, a template that is not a PDF, a link to a page beyond the last. The body is one sentence saying which.
402 Something a licence key covers. The body names what was asked for, and a key turns it on.
413 The body, or the document it would produce, runs past a ceiling the service was configured with. The body names that ceiling.
500 A failure on the service's side rather than in the request.

The reference: every key of a request

The format itself. Every figure was read off the sources of the render server and holds for the version in production.

The smallest request that draws something

One declared face, one item, one line of text. Save it, post it, and a one-page PDF comes back.

{
  "standard_fonts": [{ "name": "sans", "face": "helvetica" }],
  "items": [
    {
      "type": "text",
      "rect": { "llx": 56, "lly": 700, "urx": "{page_width} - 56", "ury": 780 },
      "content": ["Invoice 2026-014"],
      "font": "sans",
      "font_size": 24
    }
  ]
}

The same call, written three times

Copy whichever of these matches your stack. Each posts the description and one file it draws on, and writes the answer straight to disk.

From a shell

One part carries the description, one part per file it draws on. The name of a part is the name the description calls that file by.

curl -X POST https://your-server/render \
  -H "Authorization: Bearer $HQF_PDF_KEY" \
  -F "json_data=@invoice.json;type=application/json" \
  -F "letterhead.pdf=@letterhead.pdf" \
  -o invoice.pdf

From Python

The answer is the PDF itself, so write the body straight to a file. The two timing headers are there whenever the drawing succeeded.

import json
import pathlib
import requests

LETTERHEAD = pathlib.Path("letterhead.pdf")

description = {
    "standard_fonts": [{"name": "sans", "face": "helvetica"}],
    "items": [
        {
            "type": "text",
            "rect": {"llx": 56, "lly": 700, "urx": "{page_width} - 56", "ury": 780},
            "content": ["Invoice 2026-014"],
            "font": "sans",
            "font_size": 24,
        }
    ],
}

answer = requests.post(
    "https://your-server/render",
    headers={"Authorization": f"Bearer {KEY}"},
    files={
        "json_data": ("request.json", json.dumps(description), "application/json"),
        "letterhead.pdf": ("letterhead.pdf", LETTERHEAD.read_bytes()),
    },
    timeout=120,
)
answer.raise_for_status()
pathlib.Path("invoice.pdf").write_bytes(answer.content)
print(answer.headers["x-hqf-render-ms"], "ms drawing")

From JavaScript

A form body and the standard fetch, so the same lines run in a browser and in a server-side runtime. A refusal arrives as a sentence of plain text.

const description = {
  standard_fonts: [{ name: "sans", face: "helvetica" }],
  items: [
    {
      type: "text",
      rect: { llx: 56, lly: 700, urx: "{page_width} - 56", ury: 780 },
      content: ["Invoice 2026-014"],
      font: "sans",
      font_size: 24,
    },
  ],
};

const body = new FormData();
const asJson = JSON.stringify(description);
body.append("json_data", new Blob([asJson], { type: "application/json" }));
body.append("letterhead.pdf", letterheadBlob, "letterhead.pdf");

const answer = await fetch("https://your-server/render", {
  method: "POST",
  headers: { Authorization: `Bearer ${key}` },
  body,
});
if (!answer.ok) {
  throw new Error(await answer.text());
}
const pdf = new Uint8Array(await answer.arrayBuffer());

The keys of a request

Thirty-three keys stand at the top level of a request, and one of them is required. Every other key defaults to the value in the column beside it, so a request states what it needs and nothing else.

Every key a request carries at its top level
Key Type Default What it is for
items array required Everything drawn on the pages, in the order it is drawn. This is the one key a request cannot leave out.
page object 595 × 842 The sheet every page is cut to, in points, unless a page names its own.
page_sizes array [] A sheet of its own for one numbered page.
page_boxes array [] The crop, bleed, trim and art boxes of one page, which is what a printing works reads before anything else.
page_turns array [] Which way up a page is presented to whoever opens the file.
page_tab_orders array [] The order the keyboard walks the fields of one page.
fonts array [] Typefaces carried in the request as base64, each under a name the items call for.
type1_fonts array [] Type 1 typefaces, carried the same way.
standard_fonts array [] One of the fourteen faces every reader already carries, under a name of your choosing.
font_variants array [] A declared face with ligatures, small capitals, oldstyle figures or kerning switched on.
type3_fonts array [] A face whose letters are drawings rather than outlines.
font_chains array [] Two or more faces tried in order, so a letter the first lacks is taken from the next.
templates object The supplied PDFs laid under the first page, under the middle pages and under the last.
renumber array [] Where page numbering restarts, in which style, behind which prefix.
bookmarks array [] The outline a reader opens beside the page, nested as deep as you like.
destinations array [] Named places in the document a link or an action may aim at.
articles array [] Reading threads that carry a reader from one column to the next.
document_parts object The tree of parts a PDF 2.0 document declares, each claiming a run of pages and carrying named values.
metadata object Title, author, subject, producer, creation date, trapping state, and schemas of your own.
archive string The archival standard the file claims: `pdfa3b` or `pdfa4`.
invoice object The invoice XML carried inside the document, with its profile, its version, its relationship and its date.
reading object How a reader is asked to open the file: which panel, which layout, which page, which zoom, and what to send a printer.
base_uri string The address a relative link is resolved against.
version string the engine's own The PDF version the file declares: `1.4` through `1.7`, or `2.0`.
compression object Whether streams are deflated, at which level, and whether small objects travel packed together.
output_intent object sRGB The colour profile the file's colours are to be read against.
images array [] Metadata attached to one supplied picture, either stated or carried as a packet.
color_spaces array [] Separations, indexed palettes, Lab, calibrated grey and RGB, and profile-based spaces, each under a name a colour may call for.
shadings array [] Axial and radial gradients, each under a name a fill may call for.
graphics_states array [] Named states carrying an opacity for fills, an opacity for strokes and a blend mode.
layers array [] Named layers a reader can switch on and off, and which say whether they print.
drawings array [] Named vector drawings, which is what a push button shows as its icon.
transparency object The blending space the page group works in, and whether it is isolated or knocked out.

The twenty-one things an item can be

An item is an object carrying a type. Each type has a page of its own: what it is, what a developer needs to know about it, every key it takes with its default, and a whole request showing it.

Text

Tables

Pictures and imported pages

Barcodes and square codes

Shapes

Form fields

  • A box the reader types into text_field Add a box to fill in: a name, an address, a comment over several lines, a hidden password, or a number one box per digit.
  • A box the reader ticks check_box Add a tick box: accepting terms, choosing an option, with the mark you pick and the value the form reports.
  • A list the reader picks from choice_field Offer a list of answers: an open list, a drop-down menu, or a menu the reader may also type an answer into.
  • Several buttons, one answer radio_group Place buttons of which only one stays chosen at a time, each in its own spot, all belonging to the same question.
  • The place kept for a signature signature_field Keep a spot on the page where an electronic signature will settle, with its name and its frame.
  • A button the reader presses push_button Place a button that acts instead of answering: open an address, go to a page, send the form or reset it.

Navigation

The shapes every key is built from

A handful of objects recur throughout the format. They are written once here, and every page that uses one names it by these words.

The objects the keys are built from
rect Four coordinates — `llx`, `lly`, `urx`, `ury`. The origin sits at the bottom left of the sheet and the vertical axis goes up, which is the convention of the format itself.
coordinate A number, or a quoted expression over `+ - * / ( )` and the variables `{page_width}`, `{page_height}`, `{current_page}`, `{total_pages}`, `{llx}`, `{lly}`, `{urx}`, `{ury}`. An expression nests up to a hundred deep, which is how an item sits against the page width without the caller computing anything.
color One number for grey, three for red, green and blue, four for the four printing inks, all from 0 to 1. Also `{"gray": n}`, and `{"space": "name", "components": …}` for a space the request declared.
stroke A `width`, and optionally a `color`, a `dash` pattern with its phase, a `cap` and a `join`.
pages The one-based pages an item is drawn on. The default, an empty array, draws it on every page, which is how a footer is written once.
layer The declared layer an item belongs to, which is what a reader switches on and off.
id / relative_to An item names itself with `id`; another places itself against that item's `top` or `bottom` edge with an `offset`, so a paragraph follows a table whose height nobody knew in advance.
transform / rotate Six numbers for a matrix, or a number of degrees counter-clockwise about the centre of the box. An item states one or the other.

How a request is read

  • A key the server does not know is ignored, so a request written for a later version renders at an earlier one.
  • A key written twice in the same object is declined, and the message names the key.
  • A choice with no payload is written as a bare string — "center", "pdfa4". A choice carrying a payload is a single-key object naming the variant — {"points": 12}, {"page": 3}.
  • An absent optional key and an explicit null are read the same way, which is what lets a serialiser emit either.
  • The same description, posted twice, yields the same bytes twice: the file identifier is a digest of what was written, so two runs can be compared as files.

The figures the service holds itself to

The figures the service holds itself to
The body of a request 512 MiB
How deep an expression nests 100
Entries in an indexed palette 256
Compression level 1 – 9
Error correction of an Aztec code 0 – 90 %
Columns of a PDF417 code 1 – 30
Correction level of a PDF417 code 0 – 8
Faces in a chain of fonts 2 or more
Pages of the middle template 1
Characters in the name of a stored font 64
Pages in one document what your plan carries

Where to go next

How a program asks for its pages Everything the library puts on a page Everything the service keeps, in full

See the prices See the examples

Glossary

byte
The unit a file's weight is counted in, the way a parcel is counted in grams. A thousand of them make a kilobyte, and a million make a megabyte — the size of one photograph taken with a telephone. A one-page PDF here weighs between six thousand and a hundred and twenty thousand, so a hundred of them fit in the space of five photographs.
typographic point
The unit printing measures with, worth a little over a third of a millimetre: seventy-two of them make an inch. The paper, the margins and the height of the lettering are all counted in them, and so is every measurement written inside a PDF file.
standard
A rule argued out in committee, published under a number anybody may buy and read, and identical for every firm claiming it. A claim to follow one can therefore be checked against the text. A way of working that merely spread because it worked is a habit of the trade: useful, widespread, and answerable to no text at all.
Factur-X
An invoice that is a page for a person and a data file for a machine, in one document. The page looks like any invoice; inside, the same amounts are attached in a form accounting software reads without anybody retyping them. French law requires this exchange between companies.
the identity card of a file
The block inside a file that says what the file is: its title, who made it, when, and which rules it follows. Search engines and archives read it; a reader never sees it. Its technical name is XMP.
colour profile
A file that says what a colour actually looks like. Without one, the same red is one red on your screen and another on a press. A document meant for print carries the profile its colours were chosen against.
output intent
The colour profile written into a document to say which press or which screen its colours were chosen for. A printer reads it to know what the numbers in the file are meant to look like, and the rules on documents kept for the long term ask for one.
font
The drawing of every letter, digit and mark a document writes, held in a file of its own. A PDF carries the fonts it is set in inside itself, which is why it opens looking the same on a machine that has never had them. Leave them out and a reader puts another font in their place, and the layout moves.
gradient
A colour that changes across the space it fills, with no step anywhere between one end and the other: a header that fades out, a bar with some depth to it. It is described once, in a handful of numbers, and painted wherever it is wanted, so it stays sharp at any size and weighs almost nothing.
request
One call to the service: you send what the document should say, and get the document back. Your bill counts these calls, one for each document. The number of pages inside a document is never counted.
template
A PDF file made once and reused as the background of new pages: your letterhead, a plain sheet for the pages that follow, the page of terms you always attach at the end. The file is laid down exactly as it stands, to the millimetre, and the new text is written on top.
JSON
A way of writing structured information as plain text, made of named values, lists and numbers. Every programming language reads and writes it, which is why it is what a program uses to describe the document it wants.
server
A machine that waits for calls and answers them, running day and night with nobody sitting at it. The one that draws documents here holds the engine and hands back the finished pages; the one your website runs on is a different machine doing the same kind of job.
status code
The three-digit number an answer opens with, saying how the call went. Two hundred means it worked. Anything in the four hundreds means the call needs changing; anything in the five hundreds means the machine answering has something to put right.
multipart form
A way of packing several things into one call: each gets a name and travels in its own part, and they can be text or files alike. It is what a web page uses to upload a photograph, and what carries a document's description alongside the pictures it draws.
layer
A named group of things drawn on a page that a reader can switch on and off, like tracing paper laid over a plan. A drawing can hold its measurements on one and its notes on another, and a layer can be set to show on screen and stay off the printer.
watermark
Wording printed across the page itself, saying the document was made with a free account. It cannot be removed from the file; a paid tier simply never puts it there.

Every word the site explains