PDF tools

Inspect

inspect (quality report), info, perceive (perception layer).

Three read endpoints answer three different questions: what is this file (info), is it any good (inspect), and what does it say, where (perceive). All take {"pdf": "<base64>"} and return JSON.

PDF=$(base64 -w0 input.pdf)     # macOS: base64 -i input.pdf

Info — document properties

POST /v1/pdf/info:

{
  "page_count": 3,
  "version": "1.7",
  "title": "Invoice 2026-001",
  "author": "ACME",
  "encrypted": false,
  "tagged": false,
  "form": false,
  "page_sizes": [ ... ],
  "size_bytes": 182044
}

Inspect — quality report

POST /v1/pdf/inspect scores the file's production quality — the checks a print shop would run:

{
  "pages": 3,
  "bytes": 182044,
  "fonts_embedded": ["Inter-Regular"],
  "fonts_not_embedded": [],
  "objects": { "text": 214, "image": 2, "vector_path": 31, "other": 4 },
  "real_text_chars": 3211,
  "min_image_dpi": 220,
  "issues": [
    { "severity": "warning", "message": "..." }
  ],
  "score": 92
}

score is 0-100; issues carry a severity of critical, warning or info. Non-embedded fonts and low-DPI images are the classic print killers.

Perceive — the perception layer

POST /v1/pdf/perceive returns a structured, semantically-labelled map of the document — what an AI agent (or your code) reads before acting with edit-text, redact or annotate:

curl -X POST https://api.pdf-forge.dev/v1/pdf/perceive \
  -H "Authorization: Bearer $PDFFORGE_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"pdf\": \"$PDF\", \"detail\": \"full\", \"ocr\": \"auto\"}"
const doc = await client.pdf.perceive(pdfBytes, {
  detail: "full",   // "summary" (default) | "full"
  ocr: "auto",      // "auto" (default) | "off" | "force"
  pages: [1],       // optional 1-based filter
});

The result: summary, page_count, coords (the coordinate contract) and per page —

  • elements — every text span with a stable id, text, bbox [x, y, w, h] normalized 0..1 origin top-left, font/size/color, a semantic label when detected (email, phone, iban, date, amount, total, doc_number, signature_label...), its source (text or ocr) and confidence.
  • regions — logical zones (header, footer, line_items_table, totals, signature_area) with the element ids they group.

Scanned or flattened pages are OCRed automatically (ocr: "auto"); OCR elements also carry bg, the sampled background colour used for mask-and-redraw edits.

The coordinate contract

Perceive's bbox values are in the exact frame the action endpoints accept: redact regions and annotate objects use the same normalized, top-left-origin coordinates. No conversion needed.

Act on what you perceived

Perceive is step 1 of the perceive → act loop. The action endpoints:

Edit text in placePOST /v1/pdf/edit-text rewrites spans by id; the design stays intact:

{ "pdf": "<base64>", "edits": [ { "id": "0:12", "text": "New value" } ] }

Each edit accepts text, color, size, delete: true; echo the element's member_ids (merged fragments are blanked server-side). Returns the edited PDF. The SDK method is client.pdf.editText(pdf, edits).

Burn objectsPOST /v1/pdf/annotate stamps editor-style objects (text, image/signature, rect...) at normalized coordinates and returns the PDF (X-Burned-Objects header). SDK: client.pdf.annotate(pdf, objects).

RestampPOST /v1/pdf/import replaces matching text across the document ({"replacements": [{"old": "...", "new": "..."}]}) — same design, new data (X-Replaced-Objects header). SDK: client.pdf.restamp(pdf, replacements).

Lower-level reads also exist: POST /v1/pdf/extract-text (raw per-span JSON) and POST /v1/pdf/ocr (OCR spans with confidence and background colour) — perceive wraps both with semantics on top.

Next steps

On this page