# Collect the documents each case needs in n8n

Example: build the form from an insurance claim's checklist inside the request, so each person is asked for exactly the documents their case needs, then save each upload with the claim.

The same steps work for onboarding, loans, rentals, visas: anything where the paperwork depends on the case. Checked on self-hosted n8n 2.44 with version 0.13.0 of the node.

![The finished execution: Claim, Build the checklist, Ask for the documents, Wait for the documents and Completed?, then One item per file, Download each file and Save to the claim folder with 2 items each, all green](https://formstep.io/docs/guides/n8n/n8n-claim-workflow.png)

## Before you start

- The Formstep node on an n8n with a public HTTPS address, and a [Formstep credential](https://formstep.io/docs/guides/n8n/connect/).
- Somewhere to keep the files. The example writes them to n8n’s disk; the last step shows Google Drive instead.

The node is for self-hosted n8n only: n8n Cloud installs only verified community nodes, and ours is not verified yet. See [Install the Formstep node](https://formstep.io/docs/guides/n8n/install-the-node/#n8n-cloud).

Import the workflow from

[formstep-claim-documents.json](https://github.com/formstep/n8n-nodes-formstep/blob/main/examples/formstep-claim-documents.json)

with **Import from File**. It needs no form in Formstep: the request builds one.

## 1. Start from the case

The example starts with an **Edit Fields** node named _Claim_: `claimId`, `claimType` ( `water_damage` or `theft`), `claimLabel`, `firstName` and `email`. In your workflow, it is the record that opens the case, such as a CRM deal or a webhook from your claims system.

## 2. Turn the checklist into blocks

A **Code** node named _Build the checklist_ looks up the documents for the claim type and writes the form as blocks: a greeting, two [hidden fields](https://formstep.io/docs/building-forms/hidden-fields/) the greeting reads, one file question per document, and a note.

```javascript
const checklists = {
  water_damage: [
    { key: 'damage_photos', title: 'Photos of the damage', maxFiles: 10 },
    { key: 'repair_estimate', title: 'Repair estimate from a contractor', maxFiles: 1 },
  ],
  theft: [
    { key: 'police_report', title: 'Police report', maxFiles: 1 },
    { key: 'receipts', title: 'Receipts for the stolen items', maxFiles: 10 },
  ],
}
const documents = checklists[$json.claimType]
if (!documents) throw new Error(`No checklist for claim type ${$json.claimType}`)
const blocks = [
  { type: 'header', content: 'Hi {{first_name}}, a few documents for claim {{claim_id}}' },
  { type: 'paragraph', content: 'Upload each document below. Photos from your phone are fine.' },
  { type: 'hiddenField', key: 'first_name' },
  { type: 'hiddenField', key: 'claim_id' },
  ...documents.map((document) => ({ type: 'file', ...document })),
  { type: 'text', key: 'notes', title: 'Anything we should know?', required: false },
]
return { json: { ...$json, blocks } }
```

The `throw` stops the run on an unknown claim type instead of sending a form with no questions. Adding a claim type is a new entry in the object.

Each block takes `type`, a `key` and the options of its kind; `{{first_name}}` in text shows a hidden field’s value. Every block type and option is in [Create a form and a request in one call](https://formstep.io/docs/developers/inline-forms/#blocks).

## 3. Send the request with a new form

Add a Formstep **Create request** node and set **Form Source** to **New Form**:

![The Ask for the documents node: Form Source New Form, Form Name Documents for your {{ $json.claimLabel }} claim, Form Blocks {{ JSON.stringify($json.blocks) }}, Prefill {}, Context {{ JSON.stringify({ first\_name: $json.firstName, claim\_id: $json.claimId }) }}, Read-Only Field Keys empty, and under Form Options a Cover with a color](https://formstep.io/docs/guides/n8n/n8n-claim-request-node.png)

- **Form Blocks** is `{{ JSON.stringify($json.blocks) }}`, the blocks from the Code node.

- **Context** fills the hidden fields: `{{ JSON.stringify({ first_name: $json.firstName, claim_id: $json.claimId }) }}`.

- **Form Options** sets the look: a cover color, a logo and a theme.

- Set **Recipient Email**, turn on **Wait for the Outcome**, and under **Additional Fields** set **Delivery**, **Expires At** (`{{ $now.plus({ days: 14 }).toISO() }}`) and an **External ID**. **Test Mode** is on in the example.

Email invitations and reminders have plan requirements, and on Free you send the request’s `url` yourself and chase by hand: see [Invitations and reminders](https://formstep.io/docs/requests/invitations-and-reminders/). With Test Mode on, nothing is emailed until you turn it off: see [Try it on yourself first](https://formstep.io/docs/requests/creating-requests/#test-mode).

The customer sees only the documents their claim needs:

![The request page: Hi Ada, a few documents for claim CLM-3107, Upload each document below, a file upload for Photos of the damage with 10 files remaining, one for the Repair estimate from a contractor with 1 file remaining, and an optional note](https://formstep.io/docs/guides/n8n/n8n-claim-recipient.png)

> **One checklist, one form**
>
> Formstep builds a form the first time it sees a definition and reuses it after that: the second water damage claim reuses the first one’s form, and the response says `form.reused: true`. Keep what changes per person, like the name and the claim ID, in **Context** or **Prefill**, never in the blocks or the form name, or every request makes a new form. A workspace makes at most 300 new forms a day this way. See [One definition, one form](https://formstep.io/docs/developers/inline-forms/#reuse).

## 4. Wait, then make one item per file

Add a **Wait** node (**On Webhook Call**) and an **If** node that checks `{{ $json.body.type }}` is `request.completed`. Each file question’s answer is a list of files with a `name`, a `size`, a `type` and a `url`. The other output of the **If** node catches an expired or canceled request. A request expires after 30 days unless you set another date, up to 365: see [Expiry](https://formstep.io/docs/requests/invitations-and-reminders/#expiry). A **Code** node turns the files into one item per file:

```javascript
// One item per uploaded file, named after the question it answers.
const event = $json.body.data
const items = []
for (const [question, answer] of Object.entries(event.answers)) {
  if (!Array.isArray(answer)) continue
  for (const file of answer) {
    if (file.url) items.push({ json: { question, name: `${question} - ${file.name}`, url: file.url } })
  }
}
return items
```

It walks the answers instead of naming the questions, so it works for any claim type.

## 5. Download and save each file

An **HTTP Request** node downloads `{{ $json.url }}` with **Response Format** **File**, once per item. The example then writes each file with **Read/Write Files from Disk**, as `/home/node/.n8n-files/{{ $('Claim').item.json.claimId }} {{ $json.name }}`.

To keep them in Google Drive instead, replace that node with **Google Drive** › **Upload file**: Input Data Field Name `data`, File Name `{{ $json.name }}`, and the claim’s folder as Parent Folder. S3, Dropbox and SharePoint nodes take the binary field the same way. The files went to Formstep, so your form’s payload limit never applied, and each can be up to 1 GiB within your storage: see [File upload](https://formstep.io/docs/building-forms/file-uploads-signatures/#file-upload).

> **Treat a file URL like a password**
>
> Anyone with a file’s `url` can download it, so do not post it in a channel or a ticket. Download the file, as here, and share your own copy. The URL does not expire.

- The event’s `data.submission.pdfUrl`, a PDF of all the answers, is set only when the form keeps a PDF: when it has a Signature question or a webhook subscription. A request that only calls back, as here, has none.

- To send the recipient a document to read or sign as well, use a published form with a Documents block, as in [Get a manager’s approval](https://formstep.io/docs/guides/n8n/approval-from-a-manager/). A new form takes no documents.

## Next steps

[Create a form and a request in one call](https://formstep.io/docs/developers/inline-forms/) — Every block type, option and limit of a new form

[Read a PDF with AI, then get it confirmed](https://formstep.io/docs/guides/n8n/read-a-pdf-with-ai/) — Let AI fill the form, and the person who knows check it

[Let an AI agent send requests from n8n](https://formstep.io/docs/guides/n8n/ai-agent-tool/) — Give an AI Agent Formstep as a tool

[Document collection template](https://formstep.io/templates/document-collection/) — A published form with one labelled upload per file, for cases that share a checklist
