n8n-nodes-formbase 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -13
- package/dist/nodes/Formbase/Formbase.node.d.ts +12 -0
- package/dist/nodes/Formbase/Formbase.node.js +701 -0
- package/dist/nodes/Formbase/Formbase.node.js.map +1 -0
- package/dist/nodes/Formbase/Formbase.node.json +18 -0
- package/dist/nodes/Formbase/FormbaseCatalog.d.ts +26 -0
- package/dist/nodes/Formbase/FormbaseCatalog.js +44 -0
- package/dist/nodes/Formbase/FormbaseCatalog.js.map +1 -0
- package/dist/nodes/Formbase/FormbaseTrigger.node.js +26 -31
- package/dist/nodes/Formbase/FormbaseTrigger.node.js.map +1 -1
- package/dist/nodes/Formbase/GenericFunctions.d.ts +2 -2
- package/dist/nodes/Formbase/GenericFunctions.js.map +1 -1
- package/dist/nodes/Formbase/constants.d.ts +3 -0
- package/dist/nodes/Formbase/constants.js +3 -0
- package/dist/nodes/Formbase/constants.js.map +1 -1
- package/dist/package.json +6 -2
- package/examples/formbase-request-wait.json +234 -0
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# n8n-nodes-formbase
|
|
2
2
|
|
|
3
|
-
Community
|
|
3
|
+
Community nodes for [n8n](https://n8n.io) that create [formbase](https://formbase.so) requests, pause a workflow until a customer completes one, and start workflows when a request is completed, expires or is canceled, or when a form is submitted. formbase collects and verifies information from customers for workflows and AI agents: a workflow creates a request, the recipient completes a branded form without an account, and the verified answers arrive in n8n keyed by stable field keys.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
-
|
|
7
|
+
- **formbase** node: create a request for a form and a recipient, with prefilled and read-only fields, context, reminders, expiry and delivery by email; get, cancel, remind, list requests; replay a request's callback.
|
|
8
|
+
- Pause a workflow until the recipient answers: **Wait for the Outcome** points the request's callback at n8n's Wait node, so the workflow resumes with the completed, expired or canceled request as its input.
|
|
9
|
+
- **formbase Trigger** node: start a workflow when a request is completed, expires or is canceled, or when a customer submits a form. `data.request` carries the request ID and the caller's `externalId` and `metadata`, so the workflow that created the request can pick up where it left off.
|
|
8
10
|
- Trigger on abandoned submissions after a selected 12-hour, 1-day, 3-day, or 1-week idle window.
|
|
9
11
|
- Load forms dynamically from the workspace the credential is scoped to, across every page.
|
|
10
12
|
- Register and remove formbase webhook subscriptions with the n8n workflow lifecycle.
|
|
@@ -34,10 +36,50 @@ n8n registers its exact callback URL with formbase automatically through OAuth D
|
|
|
34
36
|
|
|
35
37
|
Self-hosted n8n must use configured HTTPS public URL for OAuth callback. Loopback HTTP is supported for local development.
|
|
36
38
|
|
|
37
|
-
## Use
|
|
39
|
+
## Use the formbase node
|
|
40
|
+
|
|
41
|
+
Add **formbase** to a workflow, select the **Request** resource and an operation. Every operation returns the formbase response as one item per request, with `pairedItem` set, and honours **Continue on Fail**.
|
|
42
|
+
|
|
43
|
+
| Operation | formbase method | What it does |
|
|
44
|
+
| ------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
45
|
+
| **Create** | `requests.create` | Creates a request for a published form. Returns the request summary, including the share link in `url`. |
|
|
46
|
+
| **Get** | `requests.get` | Reads one request: status, outcome, recipient, `answers` and `display` once it is completed. |
|
|
47
|
+
| **Get Many** | `requests.list` | Lists the requests of a form or of the whole workspace, newest first, with status, outcome, external ID and test filters. **Return All** follows the cursor across every page; otherwise **Limit** caps it. |
|
|
48
|
+
| **Cancel** | `requests.cancel` | Cancels a pending request, with an optional reason the recipient sees. |
|
|
49
|
+
| **Remind** | `requests.remind` | Sends the recipient a reminder email now. |
|
|
50
|
+
| **Replay Callback** | `requests.replayCallback` | Delivers the callback of a completed, expired or canceled request again, for example after n8n was down. |
|
|
51
|
+
|
|
52
|
+
### Create a request
|
|
53
|
+
|
|
54
|
+
1. Pick the **Form**. The picker lists the published and unpublished forms of the credential's workspace; a request needs a published form.
|
|
55
|
+
2. Enter the **Recipient Email** when formbase should email the link or send reminders; leave it empty for a request you hand out yourself.
|
|
56
|
+
3. Under **Prefill**, add one entry per field key and value. The key picker loads the form's prefillable fields; turn on **Parse as JSON** for a value that is not plain text, such as a repeating group's rows or a number.
|
|
57
|
+
4. Under **Context**, add values for the form's context fields: they are stored with the request and returned with the answers, but the recipient never sees them.
|
|
58
|
+
5. Under **Read-Only Fields**, pick the prefilled fields the recipient may see but not change.
|
|
59
|
+
6. In **Additional Fields**, set **Delivery** to `Email` to have formbase send the link, **Reminders** such as `2d, 5d` (leave the field empty to send none; remove it to inherit the form's schedule), **Expires At**, **Language**, **Recipient Name**, **Metadata** (a JSON object handed back with every event), **Test Mode**, or a **Callback URL** of your own.
|
|
60
|
+
7. Set **External ID** to your own identifier, for example `{{ $execution.id }}`. The node also sends it as the request's `idempotencyKey`, so a retried execution gets the same request back (`deduplicated: true`) instead of creating a second one.
|
|
61
|
+
|
|
62
|
+
Every request also carries `formId`, `status`, `url`, `externalId`, `isTest`, `deliveryStatus`, `hasCallback`, `remindersSent`, `expiresAt` and `createdAt` in its summary.
|
|
63
|
+
|
|
64
|
+
### Wait for the outcome
|
|
65
|
+
|
|
66
|
+
A request is answered minutes or days later. To pause the workflow until then:
|
|
67
|
+
|
|
68
|
+
1. On the **Create** operation, turn on **Wait for the Outcome**. The node sets the request's callback URL to `{{ $execution.resumeUrl }}`, the URL n8n's Wait node listens on for this execution. Do not set **Callback URL** at the same time; the node refuses the combination.
|
|
69
|
+
2. Add a **Wait** node right after it, with **Resume** set to **On Webhook Call** and the HTTP method left at `POST`. The execution pauses here.
|
|
70
|
+
3. When the request is completed, expires or is canceled, formbase posts the event envelope to that URL and the execution resumes. The Wait node emits the delivery as one item with the envelope under `$json.body`.
|
|
71
|
+
4. Branch on `{{ $json.body.type }}` (`request.completed`, `request.expired` or `request.canceled`) and read the answers from `{{ $json.body.data.answers.<field_key> }}`, the outcome from `{{ $json.body.data.request.outcome }}`, and the request ID from `{{ $json.body.data.request.id }}`.
|
|
72
|
+
|
|
73
|
+
[`examples/formbase-request-wait.json`](examples/formbase-request-wait.json) shows the whole pattern: formbase **Create** with **Wait for the Outcome** → **Wait** → **Switch** on the event type → **Set** reading the answers.
|
|
74
|
+
|
|
75
|
+
A resume URL only exists once the execution runs, so the test run of a Create node in the editor waits for a real answer just like a production run. Give the request an **Expires At** or a **Reminders** schedule so a forgotten request does not hold the execution open forever; an expired request resumes the workflow with `request.expired`. If n8n was unreachable when the callback fired, run **Replay Callback** for the request, or read it with **Get**: the resume URL of a finished execution is gone, so a replay only helps while the execution is still waiting.
|
|
76
|
+
|
|
77
|
+
The Wait node cannot check the `X-formbase-Signature` header that the callback carries. The resume URL is unguessable, which is what n8n relies on for every Wait node; if that is not enough for a workflow, use the trigger node instead, which verifies every delivery.
|
|
78
|
+
|
|
79
|
+
## Use the trigger
|
|
38
80
|
|
|
39
81
|
1. Add **formbase Trigger** to a workflow.
|
|
40
|
-
2. Select form and event. For an abandoned-submission event, select how long the response must remain unchanged.
|
|
82
|
+
2. Select form and event: a request that is completed, expires or is canceled, or a submission that is created or abandoned. For an abandoned-submission event, select how long the response must remain unchanged.
|
|
41
83
|
3. For a test execution, select **Listen for Test Event**, then submit the selected form.
|
|
42
84
|
4. Activate the workflow. n8n registers its production webhook with formbase and removes it when the workflow is deactivated or deleted.
|
|
43
85
|
|
|
@@ -47,9 +89,12 @@ n8n generates a separate 256-bit signing secret for each registration. Incoming
|
|
|
47
89
|
|
|
48
90
|
Abandoned-submission timing is enforced by formbase, not n8n. formbase checks incomplete responses hourly and calls the registered n8n webhook after the selected idle window, so delivery can occur up to about one hour after the threshold.
|
|
49
91
|
|
|
50
|
-
|
|
92
|
+
A completed request also creates a submission, so a **Submission Created** trigger on the same form runs for it too, with `data.request` set. Use one event or the other for a form, or branch on `{{ $json.data.request }}` in the submission workflow.
|
|
51
93
|
|
|
52
|
-
|
|
94
|
+
## Example workflows
|
|
95
|
+
|
|
96
|
+
- [`examples/formbase-request-wait.json`](examples/formbase-request-wait.json): a formbase **Create** node with **Wait for the Outcome**, a **Wait** node, a **Switch** on `request.completed` / `request.expired` / `request.canceled`, and a **Set** node that reads the request ID, outcome and an answer. Connect the credential, pick a form with a `company_name` field (or change the prefill and the Set node), and run it.
|
|
97
|
+
- [`examples/formbase-submission.json`](examples/formbase-submission.json): a **formbase Trigger** that maps event ID, event type, submission ID, respondent email, and form name into stable output fields. Connect the credential, select a form, then activate the workflow.
|
|
53
98
|
|
|
54
99
|
## Output
|
|
55
100
|
|
|
@@ -83,15 +128,46 @@ The node passes the envelope through unchanged — it does not flatten answers i
|
|
|
83
128
|
|
|
84
129
|
A submission that answered a request also carries `data.request` (`id`, and the caller's `externalId` and `metadata` when they were supplied). A response saved to PDF carries `data.submission.pdfUrl`; it is `null` when no PDF is kept. `test` is `true` for a test delivery, so a workflow can branch on it.
|
|
85
130
|
|
|
131
|
+
A request event carries the request itself in `data.request`: `id`, `status`, `outcome` (`approve`, `changes` or `decline` when the form has a decision), `externalId`, `metadata`, `context`, `recipient`, `language`, `createdAt` and `completedAt`, `expiredAt` or `canceledAt` (with `cancelReason`). A `request.completed` event also carries `form`, `submission`, `answers` and `display` exactly like a submission event; an expired or canceled request has no answers.
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"id": "evt_req123",
|
|
136
|
+
"type": "request.completed",
|
|
137
|
+
"createdAt": "2026-09-22T12:34:56.000Z",
|
|
138
|
+
"apiVersion": "2026-09-22",
|
|
139
|
+
"test": false,
|
|
140
|
+
"data": {
|
|
141
|
+
"request": {
|
|
142
|
+
"id": "req_abc123",
|
|
143
|
+
"status": "completed",
|
|
144
|
+
"outcome": "approve",
|
|
145
|
+
"externalId": "run-42",
|
|
146
|
+
"metadata": { "runId": "run-42" },
|
|
147
|
+
"context": { "case_id": "CASE-9" },
|
|
148
|
+
"recipient": { "email": "ada@acme.com", "name": "Ada" },
|
|
149
|
+
"completedAt": "2026-09-22T12:34:56.000Z"
|
|
150
|
+
},
|
|
151
|
+
"form": { "id": "frm_abc123", "name": "Vendor onboarding", "snapshotId": "snp_..." },
|
|
152
|
+
"submission": { "id": "sub_xyz789", "respondentEmail": "ada@acme.com", "submittedAt": "2026-09-22T12:34:56.000Z", "pdfUrl": null, "language": "en" },
|
|
153
|
+
"answers": { "company_name": "Acme" },
|
|
154
|
+
"display": { "company_name": "Acme" }
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
86
159
|
Delivered events use these `type` values:
|
|
87
160
|
|
|
88
|
-
| `type`
|
|
89
|
-
|
|
|
90
|
-
| `
|
|
91
|
-
| `
|
|
92
|
-
| `
|
|
161
|
+
| `type` | Meaning |
|
|
162
|
+
| ---------------------- | ------------------------------------------------------------------------ |
|
|
163
|
+
| `request.completed` | The recipient completed the request. `data.answers` holds the answers. |
|
|
164
|
+
| `request.expired` | The request reached its expiry before it was completed. |
|
|
165
|
+
| `request.canceled` | The caller canceled the request. |
|
|
166
|
+
| `submission.completed` | A respondent completed a new response. |
|
|
167
|
+
| `submission.updated` | An existing completed response changed. |
|
|
168
|
+
| `submission.abandoned` | An incomplete response reached the configured idle window. |
|
|
93
169
|
|
|
94
|
-
Webhook registration events (`submission_created` and `submission_abandoned`) select which deliveries trigger the workflow. The event `type` identifies what happened
|
|
170
|
+
Webhook registration events (`request_completed`, `request_expired`, `request_canceled`, `submission_created` and `submission_abandoned`) select which deliveries trigger the workflow. The event `type` identifies what happened. Use `id` to deduplicate retries; the same values arrive as `X-formbase-Event-Id` and `X-formbase-Event-Type` headers.
|
|
95
171
|
|
|
96
172
|
Full contracts: [formbase API methods](https://docs.formbase.so/developers/rest-api) and [webhook reference](https://docs.formbase.so/developers/webhooks-reference).
|
|
97
173
|
|
|
@@ -104,7 +180,7 @@ npm run build
|
|
|
104
180
|
npm run lint
|
|
105
181
|
```
|
|
106
182
|
|
|
107
|
-
`npm test` runs unit tests plus
|
|
183
|
+
`npm test` runs unit tests plus lifecycle tests that drive both nodes against an in-process formbase API over real HTTP (`test/fakeFormbase.mts`). `npm run dev` starts n8n with the node loaded and rebuilds on changes. Compiled package files are written to `dist/`. Run `npm pack --dry-run` before publishing to inspect package contents.
|
|
108
184
|
|
|
109
185
|
## License
|
|
110
186
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { IExecuteFunctions, ILoadOptionsFunctions, INodeExecutionData, INodePropertyOptions, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
2
|
+
export declare class Formbase implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
methods: {
|
|
5
|
+
loadOptions: {
|
|
6
|
+
getForms(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
7
|
+
getPrefillKeys(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
8
|
+
getContextKeys(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
12
|
+
}
|