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
- AcroForm forms — field types and native fill details.
- Flat forms — virtual detection on dotted-line forms.
- Render via API — the template route for recurring documents.