Dynamic & cascading fields

A dynamic field is a dropdown whose options come from a live webhook instead of a static list. Every time the form loads, FormNode calls the webhook, parses the response, and populates the options. This is what keeps your forms in sync with the source of truth — your PSA, RMM, CIPP, Hudu, or anything else with an HTTP endpoint.

A cascadingfield is a dynamic field whose webhook also receives the value of a parent field, so its options reload when the parent changes. The classic shape: pick a customer, then the site dropdown reloads to show only that customer's sites.

When you need thisStatic dropdowns drift. The moment a customer adds a site, you have to edit the form. Dynamic fields end that loop — the form always shows what the source system shows, no manual sync.
Related search pagesFor the buyer-facing version of this workflow, see dynamic dropdown forms. For the n8n-specific implementation reference, see n8n dynamic dropdown forms. For the step-by-step n8n implementation, see how to build an n8n dynamic dropdown form.

Worked example: a Microsoft 365 license dropdown

We'll build the flagship FormNode example: a new-user onboarding form whose License dropdown pulls available license SKUs (with live counts) from CIPP for the selected customer. End to end.

1. Create the field

Open your onboarding form in the builder. Drag a Dropdown field onto the canvas. In the settings panel, set the label to License and the name to license_sku. Then toggle Dynamic data on.

2. Configure the webhook

The Dynamic Data section asks for three things:

  • URL — the n8n webhook (or any HTTPS endpoint) FormNode will call. Example: https://your-n8n.example.com/webhook/cipp-licenses
  • Method — dropdown and multiselect option webhooks are called with GET. Parent-field values and trusted org context are sent as query parameters. Dynamic table workflows use POST with table field metadata in the body.
  • Response keys — set the value key and label key if your webhook does not already return { value, label } options.

3. Build the n8n workflow

On the n8n side, the workflow listens for the FormNode call, looks up the organization's CIPP tenant ID from your integration mapping, hits CIPP's /api/listlicenses endpoint, and returns a list of available license SKUs.

FormNode expects the response to look like this:

{
  "options": [
    { "value": "SPB", "label": "Microsoft 365 Business Standard (12 available)" },
    { "value": "SPE_E3", "label": "Microsoft 365 E3 (3 available)" },
    { "value": "SPE_E5", "label": "Microsoft 365 E5 (1 available)" }
  ]
}

Each option needs a value (what gets stored on submit) and a label (what the user sees). FormNode also auto-detects a few other shapes ({ "data": [...] }, plain arrays of strings, ConnectWise's { "Items": [...] }, etc.) — if your webhook returns one of those, no transform node is needed.

4. Test it

Click Test webhookin the Dynamic Data settings panel. FormNode fires a sample request with the current organization context. You'll see the raw response and the parsed options. If the parse fails, the panel surfaces the exact JSON path that didn't match a known shape — fix it in n8n, retry.

Tip — cachingDynamic dropdowns aren't cached by default; the webhook is called on every form load. For dropdowns that only change daily (license SKUs, product catalogs), cache the response in n8n with a short-TTL Set node so you're not hitting CIPP on every page view.

Cascading fields: pick a customer, filter by site

A cascading field receives the value of one or more parent fields and uses them to filter its own options. Example: an internal ticket form where you pick a customer first, then the site dropdown shows only that customer's sites from ConnectWise.

Mark the parent dependency

Add the parent field (Dropdown, dynamic or static — say, company_id). Add the child field (site_id) and toggle dynamic. In the Dynamic Data panel, expand Depends on and pick the parent field by name.

What FormNode sends

When the parent value changes, FormNode calls the child's option webhook with the parent value as a query parameter. A single parent uses parentId by default unless you configure a different parameter name:

GET /webhook/connectwise-sites?parentId=12345

What your webhook returns

Same shape as a regular dynamic field — a list of { value, label } options, just filtered by the parent:

{
  "options": [
    { "value": "1", "label": "Acme HQ — 100 Main St, Chicago IL" },
    { "value": "2", "label": "Acme Warehouse — 555 Industrial Pkwy, Joliet IL" }
  ]
}

The site dropdown reloads automatically when the company changes. If the user changes companies after picking a site, FormNode clears the stale site value so you don't end up with a Site ID from the wrong tenant in your submission.

Org-mapped dynamic fields without a visible selector

Some dynamic fields need the current organization's integration mapping, but the submitter should not have to pick a parent field first. Use orgContextParam on the dynamic field when the webhook needs a trusted mapping such as hudu.companyId, cw.companyId, or another value stored on the runtime organization.

FormNode resolves the mapping server-side after it validates the form, workspace, organization, and global-form allowlist. The browser does not provide the mapping value. If the mapping is missing or invalid, FormNode fails closed and calls the webhook without that parameter.

{
  "id": "people",
  "type": "multiselect",
  "name": "people",
  "label": "People",
  "isDynamic": true,
  "dataSourceType": "custom",
  "customWebhookUrl": "https://automation.example.com/webhook/list-people",
  "initialValueWebhookUrl": "https://automation.example.com/webhook/current-people",
  "valueKey": "id",
  "labelKey": "name",
  "orgContextParam": {
    "queryKey": "parentId",
    "mappingKey": "hudu.companyId"
  }
}

With the example above, FormNode calls the webhook as ?parentId=<organization.integrationMappings.hudu.companyId>. Use this for hidden organization context. Use Depends on only when the submitter's visible answer should filter the next field.

Add initialValueWebhookUrlwhen the field should open with the organization's current selected value. FormNode calls that URL server-side with the same org context, sanitizes the response through the prefill pipeline, and strips both webhook URLs before sending the form definition to the browser.

Matrix fields for bulk review grids (Beta)

Matrix fields use the same trusted dynamic-data path, but they load two axes instead of one option list. The result is an editable grid where rows can be skills, services, users, devices, or policy checks and columns can be members, roles, clients, or other targets.

This is best for workflow-driven forms where a human needs to review and change existing operational data before n8n writes the result back to the source system.

{
  "id": "skills_matrix",
  "type": "matrix",
  "name": "skills_matrix",
  "label": "ConnectWise skills by member",
  "isDynamic": true,
  "matrixConfig": {
    "cellType": "singleSelect",
    "rowAxis": {
      "label": "Skills",
      "sourceType": "dynamic",
      "dataSourceType": "custom",
      "customWebhookUrl": "https://automation.example.com/webhook/cw-skills",
      "valueKey": "id",
      "labelKey": "name",
      "groupKey": "category"
    },
    "columnAxis": {
      "label": "Members",
      "sourceType": "dynamic",
      "dataSourceType": "custom",
      "customWebhookUrl": "https://automation.example.com/webhook/cw-members",
      "valueKey": "id",
      "labelKey": "name"
    },
    "cellOptions": [
      { "value": "beginner", "label": "Beginner" },
      { "value": "intermediate", "label": "Intermediate" },
      { "value": "advanced", "label": "Advanced" },
      { "value": "expert", "label": "Expert" }
    ]
  },
  "initialValueWebhookUrl": "https://automation.example.com/webhook/cw-current-skills"
}

The optional initialValueWebhookUrl should return the current cells. FormNode accepts flat cell arrays, row maps, row maps with array shorthand, column maps when initialValueKeyAxis="column", dense { rows, columns, matrix } payloads, and n8n-style wrappers under data, values, cells, or results.

[
  { "rowId": "skill_123", "columnId": "member_42", "value": "advanced" },
  { "rowId": "skill_456", "columnId": "member_42", "value": "beginner" }
]

FormNode uses the loaded cells as the review baseline and can submit either the full canonical cell state or a diff-only payload, depending on the field's submissionMode.

Beta guidanceMatrix fields are designed for desktop/admin workflows first. Test the row axis, column axis, initial values, and final diff against a draft form before publishing a production workflow. See the Matrix field beta docs for limits, payload examples, and agent smoke testing.

Using a context dropdown to show the right fields

A dynamic dropdown can also drive conditional visibility. Mark it as a context selector when its selected value represents the customer, account, tenant, site, or other record the rest of the form should adapt around.

Example: your customer dropdown returns ConnectWise company IDs. When the user picks Acme Manufacturing, FormNode resolves that value to the matching context record in the workspace and can show an Active Directory section only when org.integrationMappings.aadc_server exists. Cloud-only customers never see the AD fields.

Safety behaviorField names are not guessed. The selector must be an explicitly configured Dropdown. Values can be FormNode record IDs, or unique integration mappings such as cw.companyId when the selector declares that mapping key. If a mapping value matches more than one record, FormNode fails closed and hides fields that require that context.

Real-time validation against the tenant

Dynamic fields aren't limited to populating dropdowns. You can also wire a validatorwebhook on a Text Input. Common case: validate that a proposed username doesn't already exist in the customer's Microsoft tenant before the form lets the user submit.

In the Text Input's settings panel, expand Validation → Webhook and point it at a CIPP-backed n8n workflow that returns { "valid": true } or { "valid": false, "message": "Username already taken" }. FormNode debounces the call by 400ms while the user types and shows the message inline.

Troubleshooting

Dropdown is empty

Check the n8n execution history for the option webhook and use the builder's dynamic-data test action before publishing. The most common causes:

  • Webhook returned an error status— n8n is down, auth header is wrong, or the workflow threw. The dropdown silently treats a non-2xx as "no options."
  • Response shape doesn't match — neither the options key nor a known fallback. FormNode logs the response body so you can see what it got.
  • Empty array intentionally returned — the customer genuinely has no licenses available, no sites, etc. FormNode treats this as a valid empty state, not an error.

Cascading dropdown isn't reloading

Confirm the child field's Depends on setting names the parent field by its field ID, not its label. Then check the n8n execution history — the child option webhook should run after the parent has a value, with the configured parent query parameter. If that call never appears, the child dependency is not pointing at the parent field.

Slow page loads

Dynamic fields call their webhook on form load, in parallel. If a single form has six dynamic dropdowns each making a 500ms n8n call, the page feels sluggish. Three options, in order: cache the response in n8n (short-TTL), consolidate multiple dropdowns into a single bulk-fetch webhook that returns all options at once, or move the slowest dropdown to a later page (with a Page Break) so it loads after the user has already engaged.