# Collect the documents each case needs in n8n

Turn a checklist into a form in the request itself, so every customer is asked for exactly the documents their case needs, then download every upload and save it with the case.

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://docs.formstep.io/guides/n8n/n8n-claim-workflow.png)

Import it 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.

## Before you start

- The Formstep node on an n8n with a public HTTPS address, and a [Formstep credential](https://docs.formstep.io/guides/n8n/connect/).

- Somewhere to keep the files. The example writes them to n8n’s disk; the last step shows Google Drive instead.

## 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: a CRM deal, a new row, 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://docs.formstep.io/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 } }
```

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://docs.formstep.io/requests/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://docs.formstep.io/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** gives it your brand: a cover color, a logo, 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.

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://docs.formstep.io/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 customer, 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://docs.formstep.io/requests/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`. A **Code** node turns them 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
```

## 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.

> **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 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 customer a document to read or sign as well, use a published form with a Documents block, as in [Get a manager’s approval](https://docs.formstep.io/guides/n8n/approval-from-a-manager/). A new form takes no documents.

## Next

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

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