Form import API

Add high-fidelity document imports to your form builder.

Let your customers import the forms they already use, whether they are PDFs, Word documents or pages on an existing website. Pufflet converts each form into structured JSON your editor can use.

Forms as written, not rewritten.

Checking your demo access

What high-fidelity import looks like

Source-faithful import

Pufflet preserves the original wording and structure. Review notes flag anything uncertain or unsupported by your builder.

Conditional logic preserved

Show-when rules, Other follow-ups, required settings and page breaks are returned as structured data instead of flattened text.

Builder-neutral JSON

Define what your editor supports. Elements outside those capabilities are downgraded to work within its constraints, and three mapping tables translate the JSON into your editor's element types.

Multiple inputs, one output

Import PDF, DOCX, HTML, Markdown or plain-text forms by upload or public URL.

What comes back

Each import returns one JSON document containing the converted form, review notes (each marked as needing review or as information), items set aside from the form (such as page numbers, images, and buttons), a clean or needs-review verdict, and source details. Here is an excerpt.

{
  "form": {
    "version": "1",
    "title": "Patient intake",
    "description": null,
    "sections": [
      { "id": "s2", "heading": "Insurance", "description": null, "items": [
        { "id": "q7", "kind": "single_choice",
          "label": "Do you have coverage through an employer?",
          "description": null, "required": true, "show_when": null,
          "choices": ["Yes", "No"], "other": null },
        { "id": "q8", "kind": "short_answer", "label": "Policy number",
          "description": null, "required": null, "format": null,
          "show_when": { "question": "q7", "operator": "equals", "value": "Yes" } },
        { "id": "q9", "kind": "short_answer", "label": "Attach a photo of your card",
          "description": null, "required": null, "format": null, "show_when": null }
      ] }
    ]
  },
  "review_notes": [
    { "code": "kind_downgraded", "severity": "info", "item_id": "q9",
      "message": "The original was a file upload. This editor does not support uploads, so it came through as a short answer." }
  ],
  "verification": { "status": "needs_review" },
  "source": { "format": "pdf", "page_count": 3, "char_count": 4812 }
}

Field names and structure remain consistent across all source formats.

For developers

A Pufflet integration has three parts: the configuration sent with each request, mappings from Pufflet JSON to your editor's element types, and review notes shown to your users. The examples below show how our demo builder handles each one.

How this builder maps the JSON

Like yours, our form builder has its own element types and field names. A small set of mappings translates Pufflet's JSON into that vocabulary. These are the mappings used by the demo builder.

In the JSONIn this builderNotes
form.titleText · Title, first element of page 1Editable and removable like any element.
form.descriptionText · Body, after the title
sectionsA page break before every section after the firstThe preview shows Page x of y.
section.heading / section.descriptionText · Title / Body at the top of that page
headingText · Subtitle
explanationText · Body
explanation with list: "bullet"Text · Bullet listConsecutive bullet items form one list element, one line per item. A builder that ignores the field shows each as a paragraph; one that turns off supports.formatting.lists never receives it.
short_answerQuestion · Textformat hint (email / phone / web address) kept
long_answerQuestion · Textarea
single_choiceQuestion · Single selectExactly Yes / No with no Other option becomes Yes/No.
multi_choiceQuestion · Multi select
date / time / numberQuestion · Date / Time / Number
ratingQuestion · Rating scale (from, to, end labels)
matrixQuestion · Matrix / grid (rows, columns, one or many per row)
rankingQuestion · Ranking
signatureQuestion · Signature
file_uploadNever receivedNot in this builder's supports list, so the API downgrades it to a short_answer and adds a kind_downgraded note.
unknown_questionQuestion with no type chosenThe pipeline's note is shown; pick a type.
descriptionHelper textShown only when the question has one.
[label](address) in any textA link in the textThis builder declares supports.formatting.markers.link. A builder that does not gets the label alone and a link_dropped note carrying the address. Choices never carry links.
required (true / false / null)Required checkboxfalse and null both show unchecked; an untouched value writes back unchanged.
other { label, prompt, input }Include Other: the option's wording and its answer boxprompt (a caption for the box) is carried through; it is only set when a follow-up question was merged into the option, which this page's config does not ask for.
show_whenConditional rule: show when <question> is/includes <value>The reserved value "other" means the Other option.
branchesNot copied (no branching)Removed by the API because this page's config says the builder does not support branching; a branch_dropped note says which answer would have skipped to which section.
idsKept as element idsReview notes point at elements by id.

Security and data handling

What happens to your document

Your file is read into memory, converted to text and sent to an AI provider for structuring. It is never written to disk or to our database. When the request ends, both the document and its extracted text are gone.

We do not retain the filename. It is used only to determine the file type.

Past imports cannot be retrieved through the API. Each result is returned once in response to your request. This page keeps it only in the browser tab's memory.

What we keep

We keep one record per import. It contains no document content:

Who and whataccount, API key reference, environment, stored configuration reference
Sizebytes received, characters of extracted text, page count
Resultsuccess or failure code, question count, verification status, review note codes and counts
ProcessingAI provider and model, tokens used, estimated cost, duration
VersionsAPI, pipeline and format versions

We keep this information to bill accurately, investigate reported failures and measure conversion quality over time. It contains no document text, filenames or field values.

Messages sent through this site. Messages submitted through this site's contact form are emailed to us. They are not written to our database, and the site does not retain a copy. Resend processes each message in transit on our behalf.

Importing from a URL

URL imports are limited to publicly routable HTTP and HTTPS addresses. Pufflet rejects loopback, private-range, link-local and cloud metadata addresses, as well as internal hostnames and URLs containing credentials. It follows no more than three redirects and validates every destination. Requests carry no credentials, time out after 20 seconds and stop at 20 MB.

Who else processes your document

A third-party AI provider structures each document. Because we maintain multiple fallbacks for provider outages, your document may be handled by one of the following:

  • Google (Gemini)
  • OpenAI
  • Anthropic

Every response identifies the provider and model that handled the import.

None of these providers trains its models on the data we send. Google does not use prompts or responses to improve its products on the paid Gemini tier we use. OpenAI does not train on API data unless an organisation opts in, and all data-sharing settings on our account are turned off. Anthropic does not train on commercial API traffic. Its only exception is feedback or bug reports, which we do not submit.

Processing locations. Our API runs in Virginia, USA, and our database uses the Firestore nam5 United States multi-region. The demo web page is served from Washington, DC, but documents never pass through it. Your browser uploads them directly to the API.

The AI providers are the exception. The interfaces we use do not restrict processing to a specific region, so structuring may occur anywhere those providers operate. If you require processing in a specific region, contact us to discuss your requirements.

Who we are. Pufflet is a product of Guinan Technologies, based in Toronto, Canada.

Security controls

  • All data is transmitted over HTTPS.
  • Google Cloud Firestore encrypts our database at rest by default.
  • Only our server can reach the database, using an administrative credential. Security rules deny all client access, so the database cannot be reached from a browser.
  • API keys are stored only as SHA-256 hashes. Each key is shown once when created and cannot be recovered afterwards. Revocation takes effect on the next request.
  • Each account can access only its own stored configuration. Requests containing another account's configuration reference are refused.
  • Every key is subject to a request rate limit. Demo keys also have a fixed import allowance.

Personal and regulated data

Pufflet does not currently accept personal, patient or client information in either the demo or production service. We do not yet have the agreements or controls required to process that data.

If you need to convert forms in a regulated setting, contact us before sending documents so we can discuss your requirements.

Pricing

StarterUS$49/month
  • 50 standard imports included
  • US$1 per additional standard import

One standard import covers up to 30,000 characters of form text. Longer forms count as multiple standard imports.

Higher-volume pricing

Need more than 50 standard imports per month?
Contact us for volume pricing.

Pricing shown is preliminary and may change before general availability.

Contact Pufflet
Danielle · Founder

A product of Guinan Technologies