Forms

Fill via API

form/inspect then form/fill — the full flow.

The complete flow to fill any form programmatically — native AcroForm or flat form — is two calls: inspect, then fill. The API routes each document to the right engine automatically.

Inspect the document

POST /v1/pdf/form/inspect tells you what you are dealing with:

  • form_type: "acroform" — real interactive fields; fill is native.
  • form_type: "virtual" — flat PDF, but dotted lines / checkboxes were detected as synthetic fields.
  • form_type: "xfa" — legacy XFA form (fill support depends on the file).

Use the returned fields to build your values object: field name is the key, label (virtual forms) is what you show users.

Build the values

Strings for text fields, booleans for checkbox fields. Names you omit stay empty; names that match nothing are reported as skipped.

Fill

POST /v1/pdf/form/fill with values (and flatten: true for final output) returns the filled PDF. Check X-Fields-Filled and X-Fields-Skipped.

End-to-end example

API=https://api.pdf-forge.dev
AUTH="Authorization: Bearer $PDFFORGE_API_KEY"
PDF=$(base64 -w0 form.pdf)     # macOS: base64 -i form.pdf

# 1. Inspect — list the fields
curl -s -X POST $API/v1/pdf/form/inspect \
  -H "$AUTH" -H "Content-Type: application/json" \
  -d "{\"pdf\": \"$PDF\"}" | jq '.form_type, [.fields[].name]'

# 2. Fill — values keyed by field name
curl -s -X POST $API/v1/pdf/form/fill \
  -H "$AUTH" -H "Content-Type: application/json" \
  -D headers.txt \
  -d "{
    \"pdf\": \"$PDF\",
    \"values\": {
      \"client_name\": \"SAM SARL\",
      \"city\": \"Ouagadougou\",
      \"subscribed\": true
    },
    \"flatten\": true
  }" --output filled.pdf

grep -i x-fields headers.txt
// The SDK has no dedicated forms resource yet — call the endpoints directly.
import { readFile, writeFile } from "node:fs/promises";

const API = "https://api.pdf-forge.dev";
const headers = {
  Authorization: `Bearer ${process.env.PDFFORGE_API_KEY}`,
  "Content-Type": "application/json",
};

const pdf = (await readFile("form.pdf")).toString("base64");

// 1. Inspect
const inspect = await fetch(`${API}/v1/pdf/form/inspect`, {
  method: "POST", headers, body: JSON.stringify({ pdf }),
}).then((r) => r.json());

console.log(inspect.form_type, inspect.fields.map((f: any) => f.name));

// 2. Fill
const res = await fetch(`${API}/v1/pdf/form/fill`, {
  method: "POST", headers,
  body: JSON.stringify({
    pdf,
    values: { client_name: "SAM SARL", city: "Ouagadougou", subscribed: true },
    flatten: true,
  }),
});

console.log("filled:", res.headers.get("X-Fields-Filled"));
await writeFile("filled.pdf", Buffer.from(await res.arrayBuffer()));

One PDF at a time

form/fill fills one document per call. For mass personalisation (N records → N PDFs), loop over your data — or better, use a PDF template, which is built for exactly that.

Next steps

On this page