Fill Existing PDF Forms with FormRail

A field labeled Project name in a PDF might be called Text1 internally. That detail matters when you're filling it from code. FormRail shows you the actual field names and checks your data before producing a file.

It's the new PDF API from Voltenworks. The workspace and API use the same checks, so you can try a template by hand before automating it.

Start by checking the template

FormRail works with existing AcroForm fields. A PDF that looks like a form might still be a scan or a page of printed text. You can check that before paying for a fill.

The inspection endpoint returns the exact field names, their types, required flags and available choices. If your file uses XFA, encryption or an unsupported field type, it tells you. There's no need to guess a field name from the label printed beside it.

Download the sample template and open the docs. The example has a project name, reference, notes, service choice and scheduling checkbox. Create a key in your workspace, then save it in an environment variable on your server.

Fill a form from Node.js

This example reads a local PDF, checks its compatibility, validates the data and writes the completed file. It needs Node.js with built-in fetch.

import fs from 'node:fs/promises';

const api = 'https://formrail.voltenworks.com/v1';
const headers = {
  'Content-Type': 'application/json',
  Authorization: `Bearer ${process.env.FORMRAIL_API_KEY}`,
};
const pdf = (await fs.readFile('template.pdf')).toString('base64');

async function check(endpoint, body) {
  const response = await fetch(api + endpoint, {
    method: 'POST', headers, body: JSON.stringify(body),
  });
  const report = await response.json();
  if (!response.ok) throw new Error(JSON.stringify(report));
  return report;
}

const template = await check('/inspect', { pdf });
if (!template.compatible) throw new Error(JSON.stringify(template.issues));

const body = {
  pdf,
  templateHash: template.templateHash,
  mapping: { project: 'ProjectName', ready: 'Ready' },
  data: { project: 'September installation', ready: true },
  flatten: true,
};
const validation = await check('/validate', body);
if (!validation.valid) throw new Error(JSON.stringify(validation.issues));

const response = await fetch(api + '/fill', {
  method: 'POST',
  headers: { ...headers, 'Idempotency-Key': 'request-1042-revision-1' },
  body: JSON.stringify(body),
});
if (!response.ok) throw new Error(await response.text());
await fs.writeFile('filled.pdf', Buffer.from(await response.arrayBuffer()));

The mapping lets your application use project while the PDF calls the field ProjectName. Keep the mapping and the inspection hash beside your template. If somebody replaces the PDF, a mismatched hash stops the old mapping from being applied without a review.

For another document, inspect its fields and replace this example's names and data. You can also leave out mapping and use the exact PDF field names directly.

Handle a failed request without paying twice

One successful fill uses one credit. Inspection and validation use none, and failed fills refund their credit.

Keep one idempotency key for each intended output. If the connection drops, repeat the identical request body with that key. FormRail regenerates a completed result without using another credit. Don't make a new key just because you didn't receive the first response.

After a confirmed failure, correct the problem and use a new key; the failed key keeps its stored error. If you change the data, use a new key too. Keep the JSON property order stable for retries. A request that's still running returns a conflict, so wait before retrying it.

What this version supports

The first release accepts PDFs up to 2 MiB, with at most 50 pages and 200 fields. It supports text, checkboxes, dropdowns, radio groups and lists. Validation checks required values, choices, supported characters and whether rendered text fits.

Filled text uses Noto Sans at 8 to 12 points, including Latin, Greek and Cyrillic characters. XFA, scans, encrypted files, signature fields, embedded actions and arbitrary layout editing aren't supported. Flattening removes the interactive fields; leaving it off keeps them editable. Review the returned document in either case.

The application processes PDF bytes and field values in memory. It doesn't keep copies of your documents. Your account retains credit usage and request receipts, so keep your own templates and completed files.

New accounts get 25 free fills. After that, a prepaid pack is $9 for 500 fills, or $19 per month includes 2,000 fills per paid period. Prepaid credits don't expire; monthly credits reset each paid period.

The workspace runs the same inspection, validation and fill requests as the API. I'd start there with a real template and check the downloaded PDF before adding it to an automated workflow.

More posts

Detect a Website's Booking and Payment Tools with an API
Use Voltenworks Lookup to inspect public software signals, read their supporting evidence, and handle incomplete results.
September 10, 2026
Next.js Retro-Toy Starter Template (DINK // NEXT)
DINK // NEXT is a retro-toy Next.js 15 starter with Fredoka display font, bold 3px borders, flat offset shadows, and a playful cream and cobalt palette.
April 15, 2026
Next.js Western Bug Bounty Starter Template (WANTED // NEXT)
WANTED // NEXT is a western bug bounty Next.js 15 starter with Rye display font, Courier Prime monospace, parchment and charcoal palette, wanted poster cards, and severity badges.
April 13, 2026