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.
Pufflet preserves the original wording and structure. Review notes flag anything uncertain or unsupported by your builder.
Show-when rules, Other follow-ups, required settings and page breaks are returned as structured data instead of flattened text.
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.
Import PDF, DOCX, HTML, Markdown or plain-text forms by upload or public URL.
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.
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.
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 JSON | In this builder | Notes |
|---|---|---|
form.title | Text · Title, first element of page 1 | Editable and removable like any element. |
form.description | Text · Body, after the title | |
sections | A page break before every section after the first | The preview shows Page x of y. |
section.heading / section.description | Text · Title / Body at the top of that page | |
heading | Text · Subtitle | |
explanation | Text · Body | |
explanation with list: "bullet" | Text · Bullet list | Consecutive 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_answer | Question · Text | format hint (email / phone / web address) kept |
long_answer | Question · Textarea | |
single_choice | Question · Single select | Exactly Yes / No with no Other option becomes Yes/No. |
multi_choice | Question · Multi select | |
date / time / number | Question · Date / Time / Number | |
rating | Question · Rating scale (from, to, end labels) | |
matrix | Question · Matrix / grid (rows, columns, one or many per row) | |
ranking | Question · Ranking | |
signature | Question · Signature | |
file_upload | Never received | Not in this builder's supports list, so the API downgrades it to a short_answer and adds a kind_downgraded note. |
unknown_question | Question with no type chosen | The pipeline's note is shown; pick a type. |
description | Helper text | Shown only when the question has one. |
[label](address) in any text | A link in the text | This 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 checkbox | false and null both show unchecked; an untouched value writes back unchanged. |
other { label, prompt, input } | Include Other: the option's wording and its answer box | prompt (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_when | Conditional rule: show when <question> is/includes <value> | The reserved value "other" means the Other option. |
branches | Not 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. |
ids | Kept as element ids | Review notes point at elements by id. |
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.
We keep one record per import. It contains no document content:
| Who and what | account, API key reference, environment, stored configuration reference |
|---|---|
| Size | bytes received, characters of extracted text, page count |
| Result | success or failure code, question count, verification status, review note codes and counts |
| Processing | AI provider and model, tokens used, estimated cost, duration |
| Versions | API, 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.
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.
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:
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.
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.
One standard import covers up to 30,000 characters of form text. Longer forms count as multiple standard imports.
Need more than 50 standard imports per month?
Contact us for volume pricing.
Pricing shown is preliminary and may change before general availability.
A product of Guinan Technologies