@funnelchat-hq/n8n-nodes-funnelchat 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +583 -2
- package/dist/credentials/FunnelchatApi.credentials.d.ts +10 -0
- package/dist/credentials/FunnelchatApi.credentials.js +79 -0
- package/dist/credentials/FunnelchatApi.credentials.js.map +1 -0
- package/dist/icons/funnelchat.dark.svg +1 -0
- package/dist/icons/funnelchat.svg +1 -0
- package/dist/nodes/Funnelchat/Funnelchat.node.d.ts +7 -0
- package/dist/nodes/Funnelchat/Funnelchat.node.js +170 -0
- package/dist/nodes/Funnelchat/Funnelchat.node.js.map +1 -0
- package/dist/nodes/Funnelchat/Funnelchat.node.json +18 -0
- package/dist/nodes/Funnelchat/description.d.ts +6 -0
- package/dist/nodes/Funnelchat/description.js +183 -0
- package/dist/nodes/Funnelchat/description.js.map +1 -0
- package/dist/nodes/Funnelchat/generated/operations.d.ts +4 -0
- package/dist/nodes/Funnelchat/generated/operations.js +7015 -0
- package/dist/nodes/Funnelchat/generated/operations.js.map +1 -0
- package/dist/nodes/Funnelchat/operationTypes.d.ts +54 -0
- package/dist/nodes/Funnelchat/operationTypes.js +3 -0
- package/dist/nodes/Funnelchat/operationTypes.js.map +1 -0
- package/dist/nodes/Funnelchat/request.d.ts +26 -0
- package/dist/nodes/Funnelchat/request.js +246 -0
- package/dist/nodes/Funnelchat/request.js.map +1 -0
- package/dist/nodes/FunnelchatTrigger/FunnelchatTrigger.node.d.ts +19 -0
- package/dist/nodes/FunnelchatTrigger/FunnelchatTrigger.node.js +302 -0
- package/dist/nodes/FunnelchatTrigger/FunnelchatTrigger.node.js.map +1 -0
- package/dist/nodes/FunnelchatTrigger/FunnelchatTrigger.node.json +18 -0
- package/dist/nodes/FunnelchatTrigger/catalogTypes.d.ts +7 -0
- package/dist/nodes/FunnelchatTrigger/catalogTypes.js +3 -0
- package/dist/nodes/FunnelchatTrigger/catalogTypes.js.map +1 -0
- package/dist/nodes/FunnelchatTrigger/generated/events.d.ts +2 -0
- package/dist/nodes/FunnelchatTrigger/generated/events.js +377 -0
- package/dist/nodes/FunnelchatTrigger/generated/events.js.map +1 -0
- package/dist/nodes/shared/signature.d.ts +3 -0
- package/dist/nodes/shared/signature.js +35 -0
- package/dist/nodes/shared/signature.js.map +1 -0
- package/dist/nodes/shared/transport.d.ts +23 -0
- package/dist/nodes/shared/transport.js +165 -0
- package/dist/nodes/shared/transport.js.map +1 -0
- package/dist/package.json +71 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +70 -5
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Funnelchat
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,584 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @funnelchat-hq/n8n-nodes-funnelchat
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[n8n](https://n8n.io) community node for the Funnelchat public API. With it you build workflows that work with your
|
|
4
|
+
account without writing code: sync contacts from a form or a CRM, reply to messages, check the status of your numbers
|
|
5
|
+
or react to what happens in your account.
|
|
6
|
+
|
|
7
|
+
It has three parts:
|
|
8
|
+
|
|
9
|
+
- **"Funnelchat API" credential**: your API key and the environment (production, staging or a URL of your own, such
|
|
10
|
+
as the sandbox).
|
|
11
|
+
- **"Funnelchat" node**: the API operations grouped as in the portal: contacts, conversations and messages,
|
|
12
|
+
templates, WhatsApp forms, tags, custom fields, groups, numbers and webhooks. They are generated from the unified
|
|
13
|
+
specification and from what the domains opened later.
|
|
14
|
+
- **"Funnelchat Trigger" node**: starts a workflow with each event Funnelchat sends by webhook, with its signature
|
|
15
|
+
verified.
|
|
16
|
+
|
|
17
|
+
> [!IMPORTANT]
|
|
18
|
+
> **Status: 0.1.0, ready to publish and not published yet** on npm or in n8n's node registry. It is up to date with the
|
|
19
|
+
> API running in production on 2026-10-22 (accounts `v1.4.24`, conversations `v1.18.10`). The public API is v0 and
|
|
20
|
+
> may change: see [Sources and what is left to verify](#sources-and-what-is-left-to-verify).
|
|
21
|
+
|
|
22
|
+
API documentation: [Funnelchat developer portal](https://developers-portal.funnelchat.app/docs/).
|
|
23
|
+
|
|
24
|
+
## Contents
|
|
25
|
+
|
|
26
|
+
- [Installation](#installation)
|
|
27
|
+
- [Minimal example](#minimal-example)
|
|
28
|
+
- [Credential](#credential)
|
|
29
|
+
- [The Funnelchat node](#the-funnelchat-node)
|
|
30
|
+
- [The Funnelchat Trigger](#the-funnelchat-trigger)
|
|
31
|
+
- [Workflow examples](#workflow-examples)
|
|
32
|
+
- [Testing with the sandbox](#testing-with-the-sandbox)
|
|
33
|
+
- [Regenerating](#regenerating)
|
|
34
|
+
- [Development](#development)
|
|
35
|
+
- [Sources and what is left to verify](#sources-and-what-is-left-to-verify)
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
### From n8n (community nodes)
|
|
40
|
+
|
|
41
|
+
In self-hosted n8n: **Settings → Community nodes → Install** and type `@funnelchat-hq/n8n-nodes-funnelchat`. n8n
|
|
42
|
+
downloads it from the public npm registry and makes it available in the nodes panel: search for "Funnelchat".
|
|
43
|
+
|
|
44
|
+
n8n Cloud only shows the nodes n8n has verified. The package is ready for that (strict mode, no runtime dependencies,
|
|
45
|
+
n8n lint passing), but it has not been submitted for verification yet.
|
|
46
|
+
|
|
47
|
+
### With npm, in n8n's nodes folder
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
mkdir -p ~/.n8n/nodes && cd ~/.n8n/nodes
|
|
51
|
+
npm init -y # only the first time
|
|
52
|
+
npm install @funnelchat-hq/n8n-nodes-funnelchat
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Restart n8n. With Docker, that folder is `/home/node/.n8n/nodes` inside n8n's volume. It was tested with n8n 2.25.7
|
|
56
|
+
(image `n8nio/n8n`).
|
|
57
|
+
|
|
58
|
+
### From the source code
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
git clone https://github.com/Funnelchat20/n8n-nodes-funnelchat.git
|
|
62
|
+
cd n8n-nodes-funnelchat
|
|
63
|
+
npm ci
|
|
64
|
+
npm run build
|
|
65
|
+
npm pack # creates funnelchat-hq-n8n-nodes-funnelchat-0.1.0.tgz
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Then, in n8n's nodes folder, `npm install /path/to/funnelchat-hq-n8n-nodes-funnelchat-0.1.0.tgz`.
|
|
69
|
+
|
|
70
|
+
## Minimal example
|
|
71
|
+
|
|
72
|
+
A workflow that sends a message to a contact and keeps the message ID:
|
|
73
|
+
|
|
74
|
+
1. Create the **Funnelchat API** credential with your key (see [Credential](#credential)).
|
|
75
|
+
2. Add the **Funnelchat** node — resource "Conversations and Messages", operation "Send a message to a contact":
|
|
76
|
+
- Contact ID (in the route and in the body): `2001`
|
|
77
|
+
- Number ID: the ID of the number that sends, from "Numbers → List numbers".
|
|
78
|
+
- Additional Fields → Phone: the contact's, `5491100000000`; Text: `Hello`; Client Reference: `order-981`.
|
|
79
|
+
3. Run it. You get one item with the queued message:
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"id": 1001,
|
|
84
|
+
"contact_id": 2001,
|
|
85
|
+
"device_id": 7,
|
|
86
|
+
"direction": "outbound",
|
|
87
|
+
"type": "text",
|
|
88
|
+
"status": "queued",
|
|
89
|
+
"error": null,
|
|
90
|
+
"client_reference": "order-981",
|
|
91
|
+
"created_at": "2026-10-22T13:05:00Z",
|
|
92
|
+
"updated_at": "2026-10-22T13:05:00Z"
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The result (`sent`, `delivered`, `read` or `failed`) comes later: with the **Funnelchat Trigger** and the
|
|
97
|
+
`message.status_changed` event, which carries your `client_reference`, or by reading the message with "Get a message".
|
|
98
|
+
|
|
99
|
+
## Credential
|
|
100
|
+
|
|
101
|
+
1. In Funnelchat, go to the **Developers** screen and create a key with the permissions you will use. The key is shown
|
|
102
|
+
only once and looks like `<id>|fc_live_…`.
|
|
103
|
+
2. In n8n, create a **Funnelchat API** credential:
|
|
104
|
+
- **API Key**: the full key, including the leading `<id>|`. It is sent as `Authorization: Bearer <key>`.
|
|
105
|
+
- **Environment**: **Production** (`https://api.funnelchat.com`), **Staging** (`https://api-staging.funnelchat.com`)
|
|
106
|
+
or **Custom**, with a **Base URL** of your own: for example the local sandbox, `http://localhost:4010` (see
|
|
107
|
+
[Testing with the sandbox](#testing-with-the-sandbox)). The base URL starts with `http://` or `https://`, does not
|
|
108
|
+
end in `/` and has no `/api/v1`: the node adds it. Otherwise the credential test fails and the node sends nothing.
|
|
109
|
+
3. When you save it, n8n tests the credential with `GET /api/v1/me` ("Get my account"). **That test needs the
|
|
110
|
+
`accounts:read` permission**: if your key does not have it, the test fails even if the key works for everything
|
|
111
|
+
else. You can save it anyway.
|
|
112
|
+
|
|
113
|
+
Each operation of the node says which permission it needs (`conversations:read`, `conversations:write`,
|
|
114
|
+
`conversations:send`, `communities:read`, `accounts:read`, `accounts:write`, `accounts:operate`, `webhooks:read` or
|
|
115
|
+
`webhooks:write`). If it is missing, the API answers `403 permission_denied` and the node shows the `message`, which
|
|
116
|
+
says which one is missing. All keys are `fc_live_`: what the node does acts on real data and real numbers.
|
|
117
|
+
|
|
118
|
+
## The Funnelchat node
|
|
119
|
+
|
|
120
|
+
You choose a **resource** and an **operation**. Each operation shows in its description what it is for and which
|
|
121
|
+
permission it needs.
|
|
122
|
+
|
|
123
|
+
- **Fields.** The required ones are at the top (for example, "Contact ID"). The optional ones are added from
|
|
124
|
+
**Filters** (in queries) or **Additional Fields** (in the body). Lists are typed separated by commas or come as a list
|
|
125
|
+
from an expression. Structured fields (objects, lists of objects) are written in JSON.
|
|
126
|
+
- **JSON body.** In the operations that send a body, **Send Body as JSON** lets you write it in full, as the portal's
|
|
127
|
+
reference describes it. It is useful to send `null` or complex structures.
|
|
128
|
+
- **Lists.** Paginated lists have **Return All**: when it is off, they bring up to the **Limit**. The node asks for the
|
|
129
|
+
pages itself (by page number or by cursor, depending on the route) and returns one item per element.
|
|
130
|
+
- **Responses.** An object comes out as one item; a response without a body (`204`, `202`) comes out as
|
|
131
|
+
`{ "success": true }`.
|
|
132
|
+
- **Sends.** With an API key, a send (message, template, contact card, form) answers `200` with the **queued message**
|
|
133
|
+
(`status: "queued"`) and its `id`, and the node returns that message as an item (one per message for contact cards;
|
|
134
|
+
one per target for a forward). **Client Reference** (`client_reference`, up to 64 ASCII characters without spaces)
|
|
135
|
+
comes back in the message and in `message.status_changed`. Before going out, each send goes through WhatsApp's
|
|
136
|
+
guards: the 24-hour window (`422 window_closed`, except templates), pace (`429 rate_limited`), anti-blocking
|
|
137
|
+
(`422 antiblock_duplicate_content`, `antiblock_unanswered_limit`) and monthly active contacts
|
|
138
|
+
(`429 mac_lockout_24h`, `mac_limit_reached`).
|
|
139
|
+
- **File by URL.** In "Send a message to a contact" and "Send a message", **File (URL)** accepts a URL, also signed or
|
|
140
|
+
without an extension: in that case set **File Type** (`image`, `video`, `audio`, `document` or `sticker`) and, for a
|
|
141
|
+
document, **File Name**. Without them the API answers `422` on `file_type` or `file_name`; an extension WhatsApp does
|
|
142
|
+
not support, `415`.
|
|
143
|
+
- **Errors.** The node's error carries the API's `message` and, in its description, its `error` (stable code) and the
|
|
144
|
+
response's **`X-Request-Id`**: quote it if you contact support. A `422` lists the fields with problems. A `429` with a
|
|
145
|
+
`Retry-After` of up to 60 seconds is retried on its own, up to 3 times; if the wait is longer, the error says how long
|
|
146
|
+
to wait. The API's entry point has its own errors: `404 route_not_found`, `502 origin_unreachable` and
|
|
147
|
+
`500 edge_misconfigured`. **A `502` on a write is treated as a timeout**: it may have been applied, so read the state
|
|
148
|
+
before repeating it (do not turn on **Retry On Fail** for sends). With **Continue On Fail**, the item comes out as
|
|
149
|
+
`{ "error": "<code>", "message": "<text>", "httpCode": "<status>", "requestId": "<X-Request-Id>" }`.
|
|
150
|
+
- **Notices (⚠).** Some operations show a notice above their fields: what the API's code requires and the
|
|
151
|
+
specification does not say, that a route is not open to API keys yet, or that the operation is irreversible.
|
|
152
|
+
|
|
153
|
+
The node can also be used as a tool of an n8n AI agent.
|
|
154
|
+
|
|
155
|
+
### Operations
|
|
156
|
+
|
|
157
|
+
<!-- operations:start (generated by npm run generate) -->
|
|
158
|
+
|
|
159
|
+
128 operations generated from the specification (sha256 `8bbbae569b4c`) and its supplements. The ones marked ⚠ show a notice in the node (what the code requires and the specification does not say, a route closed to keys, or an irreversible operation).
|
|
160
|
+
|
|
161
|
+
<details><summary><strong>Contacts</strong> (15)</summary>
|
|
162
|
+
|
|
163
|
+
| Operation | Route | Permission |
|
|
164
|
+
|---|---|---|
|
|
165
|
+
| List contacts "return all" | `GET /api/v1/contacts` | `conversations:read` |
|
|
166
|
+
| Create a contact | `POST /api/v1/contacts` | `conversations:write` |
|
|
167
|
+
| Create or update a contact | `POST /api/v1/contacts/update-or-create` | `conversations:write` |
|
|
168
|
+
| Set a custom field on contacts | `POST /api/v1/contacts/attach-custom-field` | `conversations:write` |
|
|
169
|
+
| Remove a custom field from contacts | `POST /api/v1/contacts/detach-custom-field` | `conversations:write` |
|
|
170
|
+
| Get a contact | `GET /api/v1/contacts/{contact}` | `conversations:read` |
|
|
171
|
+
| Update a contact | `PUT /api/v1/contacts/{contact}` | `conversations:write` |
|
|
172
|
+
| List a contact's tags "return all" | `GET /api/v1/contacts/{contact}/tags` | `conversations:read` |
|
|
173
|
+
| List a contact's custom fields "return all" | `GET /api/v1/contacts/{contact}/custom-fields` | `conversations:read` |
|
|
174
|
+
| Get a contact's 24-hour window | `GET /api/v1/contacts/{contact}/meta-window` | `conversations:read` |
|
|
175
|
+
| List a number's contacts "return all" | `GET /api/v1/devices/{device}/contacts` | `conversations:read` |
|
|
176
|
+
| Delete contacts | `POST /api/v1/contacts/delete` | `conversations:write` |
|
|
177
|
+
| List a contact's notes "return all" | `GET /api/v1/contacts/{contact}/notes` | `conversations:read` |
|
|
178
|
+
| Add a note to a contact | `POST /api/v1/contacts/{contact}/notes` | `conversations:write` |
|
|
179
|
+
| Delete a note | `DELETE /api/v1/contacts/{contact}/notes/{note}` | `conversations:write` |
|
|
180
|
+
|
|
181
|
+
</details>
|
|
182
|
+
|
|
183
|
+
<details><summary><strong>Conversations and Messages</strong> (18)</summary>
|
|
184
|
+
|
|
185
|
+
| Operation | Route | Permission |
|
|
186
|
+
|---|---|---|
|
|
187
|
+
| List conversations "return all" | `GET /api/v1/conversations` | `conversations:read` |
|
|
188
|
+
| Count unread conversations | `GET /api/v1/conversations/count/unread` | `conversations:read` |
|
|
189
|
+
| Count open conversations | `GET /api/v1/conversations/count/open` | `conversations:read` |
|
|
190
|
+
| Count closed conversations | `GET /api/v1/conversations/count/closed` | `conversations:read` |
|
|
191
|
+
| Count all conversations | `GET /api/v1/conversations/count/all` | `conversations:read` |
|
|
192
|
+
| Mark all as read | `POST /api/v1/conversations/mark-all-read` | `conversations:write` |
|
|
193
|
+
| Mark all as unread | `POST /api/v1/conversations/mark-all-unread` | `conversations:write` |
|
|
194
|
+
| Update a contact's conversation | `PUT /api/v1/contacts/{contact}/update` | `conversations:write` |
|
|
195
|
+
| Send a message to a contact ⚠ | `POST /api/v1/contacts/{contact}/message` | `conversations:send` |
|
|
196
|
+
| List a contact's messages "return all" | `GET /api/v1/contacts/{contactId}/messages` | `conversations:read` |
|
|
197
|
+
| Send a message ⚠ | `POST /api/v1/message/send-message` | `conversations:send` |
|
|
198
|
+
| Get a message | `GET /api/v1/contacts/{contact}/messages/{message}` | `conversations:read` |
|
|
199
|
+
| React to a message ⚠ | `PUT /api/v1/contacts/{contact}/messages/{message}/reaction` | `conversations:send` |
|
|
200
|
+
| Remove a reaction ⚠ | `DELETE /api/v1/contacts/{contact}/messages/{message}/reaction` | `conversations:send` |
|
|
201
|
+
| Forward a message ⚠ | `POST /api/v1/contacts/{contact}/messages/{message}/forward` | `conversations:send` |
|
|
202
|
+
| Send a poll ⚠ | `POST /api/v1/contacts/{contact}/message/poll` | `conversations:send` |
|
|
203
|
+
| Send an event invitation ⚠ | `POST /api/v1/contacts/{contact}/message/event` | `conversations:send` |
|
|
204
|
+
| Send a contact card ⚠ | `POST /api/v1/contacts/{contact}/message/contact-card` | `conversations:send` |
|
|
205
|
+
|
|
206
|
+
</details>
|
|
207
|
+
|
|
208
|
+
<details><summary><strong>Templates</strong> (7)</summary>
|
|
209
|
+
|
|
210
|
+
| Operation | Route | Permission |
|
|
211
|
+
|---|---|---|
|
|
212
|
+
| List templates "return all" | `GET /api/v1/templates` | `conversations:read` |
|
|
213
|
+
| Create a template | `POST /api/v1/templates` | `conversations:write` |
|
|
214
|
+
| Get a template | `GET /api/v1/templates/{template}` | `conversations:read` |
|
|
215
|
+
| Update a template | `PUT /api/v1/templates/{template}` | `conversations:write` |
|
|
216
|
+
| Get a template's quick reply buttons | `GET /api/v1/templates/{template}/quick-reply-buttons` | `conversations:read` |
|
|
217
|
+
| Send a template ⚠ | `POST /api/v1/devices/{device}/templates/{template}/send-template` | `conversations:send` |
|
|
218
|
+
| Delete a template from a number | `DELETE /api/v1/devices/{device}/templates/{template}` | `conversations:write` |
|
|
219
|
+
|
|
220
|
+
</details>
|
|
221
|
+
|
|
222
|
+
<details><summary><strong>WhatsApp Forms</strong> (19)</summary>
|
|
223
|
+
|
|
224
|
+
| Operation | Route | Permission |
|
|
225
|
+
|---|---|---|
|
|
226
|
+
| List forms "return all" | `GET /api/v1/forms` | `conversations:read` |
|
|
227
|
+
| Create a form | `POST /api/v1/forms` | `conversations:write` |
|
|
228
|
+
| List form templates | `GET /api/v1/forms/templates` | `conversations:read` |
|
|
229
|
+
| Get a form | `GET /api/v1/forms/{id}` | `conversations:read` |
|
|
230
|
+
| Save a form | `PUT /api/v1/forms/{id}` | `conversations:write` |
|
|
231
|
+
| Delete a draft form | `DELETE /api/v1/forms/{id}` | `conversations:write` |
|
|
232
|
+
| Get a form's numbers | `GET /api/v1/forms/{id}/numbers` | `conversations:read` |
|
|
233
|
+
| Add a number to a form | `POST /api/v1/forms/{id}/numbers` | `conversations:write` |
|
|
234
|
+
| Remove a number from a form | `DELETE /api/v1/forms/{id}/numbers/{deviceId}` | `conversations:write` |
|
|
235
|
+
| Retry publishing on the numbers | `POST /api/v1/forms/{id}/numbers/retry` | `conversations:write` |
|
|
236
|
+
| Create a new version of a form | `POST /api/v1/forms/{id}/versions` | `conversations:write` |
|
|
237
|
+
| Validate a form with Meta | `POST /api/v1/forms/{id}/sync` | `conversations:write` |
|
|
238
|
+
| Publish a form | `POST /api/v1/forms/{id}/publish` | `conversations:write` |
|
|
239
|
+
| Get a form's published definition | `GET /api/v1/forms/families/{familyId}/definition` | `conversations:read` |
|
|
240
|
+
| List a form's versions "return all" | `GET /api/v1/forms/families/{familyId}/versions` | `conversations:read` |
|
|
241
|
+
| List a form's responses "return all" | `GET /api/v1/forms/families/{familyId}/responses` | `conversations:read` |
|
|
242
|
+
| Get a form's response | `GET /api/v1/forms/families/{familyId}/responses/{runId}` | `conversations:read` |
|
|
243
|
+
| Get a form's metrics | `GET /api/v1/forms/families/{familyId}/metrics` | `conversations:read` |
|
|
244
|
+
| Send a form to a contact ⚠ | `POST /api/v1/contacts/{contact}/message/form` | `conversations:send` |
|
|
245
|
+
|
|
246
|
+
</details>
|
|
247
|
+
|
|
248
|
+
<details><summary><strong>Tags</strong> (6)</summary>
|
|
249
|
+
|
|
250
|
+
| Operation | Route | Permission |
|
|
251
|
+
|---|---|---|
|
|
252
|
+
| List tags "return all" | `GET /api/v1/tags` | `conversations:read` |
|
|
253
|
+
| Create a tag | `POST /api/v1/tags` | `conversations:write` |
|
|
254
|
+
| Get a tag | `GET /api/v1/tags/{tag}` | `conversations:read` |
|
|
255
|
+
| Update a tag | `PUT /api/v1/tags/{tag}` | `conversations:write` |
|
|
256
|
+
| List the contacts with a tag "return all" | `GET /api/v1/tags/{tag}/contacts` | `conversations:read` |
|
|
257
|
+
| Delete tags | `POST /api/v1/tags/delete` | `conversations:write` |
|
|
258
|
+
|
|
259
|
+
</details>
|
|
260
|
+
|
|
261
|
+
<details><summary><strong>Custom Fields</strong> (5)</summary>
|
|
262
|
+
|
|
263
|
+
| Operation | Route | Permission |
|
|
264
|
+
|---|---|---|
|
|
265
|
+
| List custom fields "return all" | `GET /api/v1/custom-fields` | `conversations:read` |
|
|
266
|
+
| Create a custom field | `POST /api/v1/custom-fields` | `conversations:write` |
|
|
267
|
+
| Delete custom fields | `POST /api/v1/custom-fields/delete` | `conversations:write` |
|
|
268
|
+
| Get a custom field | `GET /api/v1/custom-fields/{customField}` | `conversations:read` |
|
|
269
|
+
| Update a custom field | `PUT /api/v1/custom-fields/{customField}` | `conversations:write` |
|
|
270
|
+
|
|
271
|
+
</details>
|
|
272
|
+
|
|
273
|
+
<details><summary><strong>Groups</strong> (14)</summary>
|
|
274
|
+
|
|
275
|
+
| Operation | Route | Permission |
|
|
276
|
+
|---|---|---|
|
|
277
|
+
| List groups "return all" | `GET /api/v1/groups` | `communities:read` |
|
|
278
|
+
| List the groups a number manages "return all" | `GET /api/v1/devices/{device}/groups` | `communities:read` |
|
|
279
|
+
| List a number's announcement groups "return all" | `GET /api/v1/devices/{device}/ad-groups` | `communities:read` |
|
|
280
|
+
| List a number's channels "return all" | `GET /api/v1/devices/{device}/channels` | `communities:read` |
|
|
281
|
+
| Get a group's current data on WhatsApp | `GET /api/v1/groups/{groupUid}/metadata` | `communities:read` |
|
|
282
|
+
| Get a group | `GET /api/v1/groups/{group}` | `communities:read` |
|
|
283
|
+
| List the session actions on a group "return all" | `GET /api/v1/groups/{group}/actions` | `communities:read` |
|
|
284
|
+
| List the messages sent to a group "return all" | `GET /api/v1/groups/{group}/messages` | `communities:read` |
|
|
285
|
+
| List the scheduled sends that include a group "return all" | `GET /api/v1/groups/{group}/scheduled-messages` | `communities:read` |
|
|
286
|
+
| List a campaign's participants "return all" | `GET /api/v1/campaigns/{campaign}/participants` | `communities:read` |
|
|
287
|
+
| List participants with their current group "return all" | `GET /api/v1/campaigns/{campaign}/campaign-participants` | `communities:read` |
|
|
288
|
+
| List a campaign's joins and leaves "return all" | `GET /api/v1/campaigns/{campaign}/participant-events` | `communities:read` |
|
|
289
|
+
| List a group's participants "return all" | `GET /api/v1/groups/participants` | `communities:read` |
|
|
290
|
+
| List a group's joins and leaves "return all" | `GET /api/v1/groups/{group}/participant-events` | `communities:read` |
|
|
291
|
+
|
|
292
|
+
</details>
|
|
293
|
+
|
|
294
|
+
<details><summary><strong>Numbers</strong> (29)</summary>
|
|
295
|
+
|
|
296
|
+
| Operation | Route | Permission |
|
|
297
|
+
|---|---|---|
|
|
298
|
+
| List numbers "return all" | `GET /api/v1/devices` | `accounts:read` |
|
|
299
|
+
| Get a number's WhatsApp profile | `GET /api/v1/devices/{device}/me` | `accounts:read` |
|
|
300
|
+
| Check whether a phone has WhatsApp | `GET /api/v1/devices/{device}/check-phone` | `accounts:read` |
|
|
301
|
+
| Get a number | `GET /api/v1/devices/{device}` | `accounts:read` |
|
|
302
|
+
| Update a number | `PUT /api/v1/devices/{device}` | `accounts:write` |
|
|
303
|
+
| Delete a number ⚠ | `DELETE /api/v1/devices/{device}` | `accounts:operate` |
|
|
304
|
+
| Restart a number's session ⚠ | `PUT /api/v1/devices/{device}/reset` | `accounts:operate` |
|
|
305
|
+
| Set the received messages webhook | `PUT /api/v1/devices/{device}/update-webhook-received` | `accounts:write` |
|
|
306
|
+
| Set the received and delivery webhook | `PUT /api/v1/devices/{device}/update-webhook-received-and-delivery` | `accounts:write` |
|
|
307
|
+
| Clean a number ⚠ | `POST /api/v1/devices/{device}/clean` | `accounts:operate` |
|
|
308
|
+
| List a number's chats | `GET /api/v1/devices/{device}/chats` | `accounts:read` |
|
|
309
|
+
| Check which phones have an open conversation | `GET /api/v1/devices/{device}/open-conversations` | `accounts:read` |
|
|
310
|
+
| Get the message queue | `GET /api/v1/devices/{device}/message-queue` | `accounts:read` |
|
|
311
|
+
| Delete messages from the queue | `DELETE /api/v1/devices/{device}/message-queue` | `accounts:write` |
|
|
312
|
+
| Count the messages in the queue | `GET /api/v1/devices/{device}/message-queue/count` | `accounts:read` |
|
|
313
|
+
| Empty the message queue ⚠ | `DELETE /api/v1/devices/{device}/message-queue/all` | `accounts:operate` |
|
|
314
|
+
| Get the connection status | `GET /api/v1/devices/{device}/status` | `accounts:read` |
|
|
315
|
+
| Get a token for the WhatsApp Web extension | `POST /api/v1/devices/{device}/extension-token` | `accounts:write` |
|
|
316
|
+
| Get a number's health | `GET /api/v1/devices/{device}/health` | `accounts:read` |
|
|
317
|
+
| Refresh a number's health | `POST /api/v1/devices/{device}/health/refresh` | `accounts:write` |
|
|
318
|
+
| Get the connection history "return all" | `GET /api/v1/devices/{device}/connection-history` | `accounts:read` |
|
|
319
|
+
| List a number's logs "return all" | `GET /api/v1/devices/{device}/logs` | `accounts:read` |
|
|
320
|
+
| Get the logs health signal | `GET /api/v1/devices/{device}/logs/health` | `accounts:read` |
|
|
321
|
+
| List the logs of all numbers "return all" | `GET /api/v1/devices/logs` | `accounts:read` |
|
|
322
|
+
| Get the username | `GET /api/v1/devices/{device}/username` | `accounts:read` |
|
|
323
|
+
| Set the username | `POST /api/v1/devices/{device}/username` | `accounts:write` |
|
|
324
|
+
| Suggest usernames | `GET /api/v1/devices/{device}/username/suggestions` | `accounts:read` |
|
|
325
|
+
| Get the disconnection survey | `GET /api/v1/devices/{device}/disconnect-survey` | `accounts:read` |
|
|
326
|
+
| Answer the disconnection survey | `POST /api/v1/devices/{device}/disconnect-survey` | `accounts:write` |
|
|
327
|
+
|
|
328
|
+
</details>
|
|
329
|
+
|
|
330
|
+
<details><summary><strong>Webhooks</strong> (15)</summary>
|
|
331
|
+
|
|
332
|
+
| Operation | Route | Permission |
|
|
333
|
+
|---|---|---|
|
|
334
|
+
| List webhook subscriptions ⚠ | `GET /api/v1/webhooks` | `webhooks:read` |
|
|
335
|
+
| Create a webhook subscription ⚠ | `POST /api/v1/webhooks` | `webhooks:write` |
|
|
336
|
+
| Get a webhook subscription ⚠ | `GET /api/v1/webhooks/{id}` | `webhooks:read` |
|
|
337
|
+
| Update a webhook subscription ⚠ | `PUT /api/v1/webhooks/{id}` | `webhooks:write` |
|
|
338
|
+
| Delete a webhook subscription ⚠ | `DELETE /api/v1/webhooks/{id}` | `webhooks:write` |
|
|
339
|
+
| List a subscription's deliveries ⚠ "return all" | `GET /api/v1/webhooks/{id}/deliveries` | `webhooks:read` |
|
|
340
|
+
| Send a test event ⚠ | `POST /api/v1/webhooks/{id}/test` | `webhooks:write` |
|
|
341
|
+
| List event types | `GET /api/v1/webhooks/events` | `accounts:read` |
|
|
342
|
+
| List webhooks "return all" | `GET /api/v1/webhooks/endpoints` | `accounts:read` |
|
|
343
|
+
| Create a webhook | `POST /api/v1/webhooks/endpoints` | `accounts:write` |
|
|
344
|
+
| Get a webhook | `GET /api/v1/webhooks/endpoints/{id}` | `accounts:read` |
|
|
345
|
+
| Update a webhook | `PATCH /api/v1/webhooks/endpoints/{id}` | `accounts:write` |
|
|
346
|
+
| Delete a webhook | `DELETE /api/v1/webhooks/endpoints/{id}` | `accounts:write` |
|
|
347
|
+
| Choose a webhook's events | `PUT /api/v1/webhooks/endpoints/{id}/events` | `accounts:write` |
|
|
348
|
+
| List a webhook's deliveries "return all" | `GET /api/v1/webhooks/endpoints/{id}/deliveries` | `accounts:read` |
|
|
349
|
+
|
|
350
|
+
</details>
|
|
351
|
+
|
|
352
|
+
<!-- operations:end -->
|
|
353
|
+
|
|
354
|
+
## The Funnelchat Trigger
|
|
355
|
+
|
|
356
|
+
### How it works
|
|
357
|
+
|
|
358
|
+
1. You choose the **Events** from the list. The list comes from Funnelchat's catalog (`GET /api/v1/webhooks/events`):
|
|
359
|
+
it shows the types that can be subscribed to, the read permission each one needs, and marks the ones that are not
|
|
360
|
+
emitted yet (`planned`). If the key cannot read the catalog (it needs `accounts:read`), it shows the list built into
|
|
361
|
+
the node, copied from the catalog of accounts `v1.4.24`: 53 types, among them `message.received`,
|
|
362
|
+
`message.status_changed`, `whatsapp_form.completed`, `group.message_received`, `group_action.completed` and
|
|
363
|
+
`group.reaction_added`.
|
|
364
|
+
2. When the workflow is **activated**, the node creates a subscription in Funnelchat (`POST /api/v1/webhooks`) with
|
|
365
|
+
n8n's URL, those events and the description `n8n · <workflow name>`, and keeps the `id` and the secret Funnelchat
|
|
366
|
+
returns only once.
|
|
367
|
+
3. Each delivery comes signed. The node **verifies the signature** before starting the workflow and **rejects with
|
|
368
|
+
`400`** the ones that are not valid: the workflow does not run.
|
|
369
|
+
4. When the workflow is **deactivated**, the node deletes its subscription (`DELETE /api/v1/webhooks/{id}`). It only
|
|
370
|
+
touches the one it created: the account's other subscriptions are left alone.
|
|
371
|
+
|
|
372
|
+
If you change the events or the filter and activate again, the node updates its subscription in place
|
|
373
|
+
(`PUT /api/v1/webhooks/{id}`) and the secret does not change. If the subscription no longer exists or was disabled, it
|
|
374
|
+
creates another one. **Listen for test event** creates a separate subscription for n8n's test URL and deletes it when
|
|
375
|
+
done.
|
|
376
|
+
|
|
377
|
+
**Options:** a **Description** of your own to recognize the subscription, and **Campaign IDs** to receive only the
|
|
378
|
+
events of those campaigns (`filters.campaign_ids`; with a filter, an event without a campaign does not arrive).
|
|
379
|
+
|
|
380
|
+
### What you need
|
|
381
|
+
|
|
382
|
+
- **A public `https` URL for n8n** (with Production or Staging). Funnelchat rejects `http` URLs, internal addresses
|
|
383
|
+
and URLs with a user and password. In self-hosted n8n, set `WEBHOOK_URL` to your domain or use a tunnel. With a
|
|
384
|
+
custom base URL (the sandbox), n8n's local URL works too, such as `http://localhost:5678`: the node does not filter
|
|
385
|
+
the URL, the API decides.
|
|
386
|
+
- **A key of the account owner**, with `webhooks:read` and `webhooks:write` and the read permission of the area of each
|
|
387
|
+
event you choose (for example, `conversations:read` for `message.received`, `communities:read` for the group events).
|
|
388
|
+
Without the one of an area, creating the subscription answers `422` on `events`.
|
|
389
|
+
- **Room in the account:** there is a cap of 50 subscriptions per account.
|
|
390
|
+
|
|
391
|
+
### What reaches the workflow
|
|
392
|
+
|
|
393
|
+
One item per delivery, with the event envelope as Funnelchat sends it:
|
|
394
|
+
|
|
395
|
+
```json
|
|
396
|
+
{
|
|
397
|
+
"id": "evt_000000000000000000000001",
|
|
398
|
+
"type": "message.received",
|
|
399
|
+
"version": 1,
|
|
400
|
+
"occurred_at": "2026-10-01T12:00:00Z",
|
|
401
|
+
"data": {
|
|
402
|
+
"message": {
|
|
403
|
+
"id": 1001,
|
|
404
|
+
"type": "text",
|
|
405
|
+
"text": "Hi, I'd like more information",
|
|
406
|
+
"media": null
|
|
407
|
+
},
|
|
408
|
+
"contact": { "id": 2001, "name": "Sample contact", "phone": "+5491100000000" },
|
|
409
|
+
"receiver": { "phone": "+5491100000001" },
|
|
410
|
+
"device_id": 4521
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
Delivery is "at least once": the same event may arrive twice with the same `id`. If your workflow does not tolerate
|
|
416
|
+
duplicates, keep the processed `id`s and drop the ones you have already seen. The format of each type is in the
|
|
417
|
+
portal's event catalog.
|
|
418
|
+
|
|
419
|
+
### The signature
|
|
420
|
+
|
|
421
|
+
Header `X-Funnelchat-Signature: t=<unix>,v1=<hex>`. `v1` is the HMAC-SHA256, with the full secret of the subscription
|
|
422
|
+
(including its `whsec_`), of `"<t>." + raw body`. There may be several `v1` (one match is enough) and `t` cannot be more
|
|
423
|
+
than 300 seconds away from n8n's clock: keep the server's clock in sync. The code is the one of the portal's snippet
|
|
424
|
+
(`snippets/verificar-firma.js`) and the tests use its same vectors (`tests/test_firmas.py`).
|
|
425
|
+
|
|
426
|
+
## Workflow examples
|
|
427
|
+
|
|
428
|
+
### Receive a message and reply
|
|
429
|
+
|
|
430
|
+
1. **Funnelchat Trigger** — Events: "Message received (message.received)".
|
|
431
|
+
2. **If** (optional) — continue only if `{{ $json.data.message.type }}` is `text`.
|
|
432
|
+
3. **Funnelchat** — resource "Conversations and Messages", operation "Send a message to a contact":
|
|
433
|
+
- Contact ID (in the route and in the body): `{{ $json.data.contact.id }}`
|
|
434
|
+
- Number ID: `{{ $json.data.device_id }}` (it may be `null`; then, the ID from "Numbers → List numbers").
|
|
435
|
+
- Additional Fields → Phone: `{{ $json.data.contact.phone }}`. With an API key it is required (or WhatsApp ID) and
|
|
436
|
+
must be the contact's: otherwise, `422 recipient_mismatch`.
|
|
437
|
+
- Additional Fields → Text: `Thanks for writing to us! We will get back to you shortly.`
|
|
438
|
+
|
|
439
|
+
Reply within the contact's 24-hour window: outside it, WhatsApp requires a template ("Templates → Send a template") and
|
|
440
|
+
the send answers `422 window_closed`.
|
|
441
|
+
|
|
442
|
+
### Send a WhatsApp form and receive the answers
|
|
443
|
+
|
|
444
|
+
1. **Funnelchat** — resource "WhatsApp Forms", operation "Send a form to a contact": Contact ID, **Form ID** (the
|
|
445
|
+
`family_id` from "List forms", not the ID of a version) and the ID of a number where it is published. You get the
|
|
446
|
+
queued message, of type `whatsapp_form`.
|
|
447
|
+
2. In another workflow, **Funnelchat Trigger** with the event "WhatsApp form completed (whatsapp_form.completed)": it
|
|
448
|
+
comes with `data.form`, `data.contact_id`, `data.message_id` (the ID the send returned) and `data.answers`, a list
|
|
449
|
+
of `{key, label, value}`.
|
|
450
|
+
|
|
451
|
+
Forms are built, validated with Meta and published with the other operations of the resource ("Create a form", "Save a
|
|
452
|
+
form", "Validate a form with Meta", "Publish a form").
|
|
453
|
+
|
|
454
|
+
### Create a contact from a form
|
|
455
|
+
|
|
456
|
+
1. **n8n Form Trigger** — fields "Name", "Email" and "Phone" (or a **Webhook** that receives your form).
|
|
457
|
+
2. **Funnelchat** — resource "Contacts", operation "Create or update a contact":
|
|
458
|
+
- Phone: `{{ $json["Phone"] }}`, with the country code (with or without `+`).
|
|
459
|
+
- Additional Fields → Name: `{{ $json["Name"] }}`, Email: `{{ $json["Email"] }}`.
|
|
460
|
+
|
|
461
|
+
If the phone already exists, it updates that contact; otherwise it creates it. It is safe to retry.
|
|
462
|
+
|
|
463
|
+
3. **Funnelchat** (optional) — operation "Add a note to a contact": Contact ID `{{ $json.id }}`, Note Text
|
|
464
|
+
`Came from the website form`.
|
|
465
|
+
|
|
466
|
+
To find that contact by phone later, remove the `+`: the API stores the phone without `+` and the search by `phone`
|
|
467
|
+
does not remove it.
|
|
468
|
+
|
|
469
|
+
## Testing with the sandbox
|
|
470
|
+
|
|
471
|
+
[`funnelchat-sandbox`](https://github.com/Funnelchat20/funnelchat-sandbox) imitates the public API on your machine,
|
|
472
|
+
with in-memory state and signed webhooks, and has no real effects: nothing goes out to WhatsApp. How to start it is in
|
|
473
|
+
its README. By default it listens on `http://localhost:4010`.
|
|
474
|
+
|
|
475
|
+
1. **Start the sandbox**, for example with `npx @funnelchat-hq/sandbox` or
|
|
476
|
+
`docker run --rm -p 4010:4010 funnelchat-sandbox`. Its panel is at `http://localhost:4010/_sandbox`.
|
|
477
|
+
2. **Create a Funnelchat API credential** for the sandbox:
|
|
478
|
+
- **Environment**: **Custom**.
|
|
479
|
+
- **Base URL**: `http://localhost:4010`. If n8n runs in Docker and the sandbox on your machine,
|
|
480
|
+
`http://host.docker.internal:4010` (with Podman, `http://host.containers.internal:4010`).
|
|
481
|
+
- **API Key**: `1|fc_live_sandbox`. The sandbox accepts any key shaped like a real one, and all of them have every
|
|
482
|
+
permission.
|
|
483
|
+
|
|
484
|
+
The credential test calls the sandbox's `GET /api/v1/me`.
|
|
485
|
+
|
|
486
|
+
3. **Actions.** Any operation of the node works against the sandbox. Contacts, conversations and messages, tags, custom
|
|
487
|
+
fields, templates, WhatsApp forms, numbers and webhook subscriptions keep what you do; the rest answers an example
|
|
488
|
+
built from the specification. Sending with an API key is open in the sandbox, with the API's 24-hour window and
|
|
489
|
+
anti-spam guards, and every response carries `X-Request-Id`.
|
|
490
|
+
4. **Trigger.** The sandbox accepts `http` and local URLs, so an n8n on `http://localhost:5678` receives the events.
|
|
491
|
+
Activate the workflow with the **Funnelchat Trigger** and simulate an incoming message:
|
|
492
|
+
|
|
493
|
+
```bash
|
|
494
|
+
curl -s -X POST http://localhost:4010/_sandbox/inbound/message \
|
|
495
|
+
-H 'Content-Type: application/json' \
|
|
496
|
+
-d '{"phone": "5491100000000", "device_id": 3, "text": "Hello"}'
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
The sandbox signs the delivery like the API, the node verifies the signature and the workflow starts with the
|
|
500
|
+
`message.received` event. If n8n runs in Docker, set its `WEBHOOK_URL` to a URL the sandbox can reach (for example
|
|
501
|
+
`http://localhost:5678/` publishing that port, or `http://host.docker.internal:5678/` if the sandbox runs in Docker
|
|
502
|
+
too).
|
|
503
|
+
|
|
504
|
+
5. **What the sandbox does not show you:** with the real API, n8n's URL has to be `https` and public (try it with
|
|
505
|
+
`npx @funnelchat-hq/sandbox --require-https`).
|
|
506
|
+
|
|
507
|
+
The end-to-end tests (`npm run test:e2e`, see [Development](#development)) passed on 2026-10-22 against the sandbox on
|
|
508
|
+
`feat/inicial` @ `7e244b8`: send and read a message, file by URL, `X-Request-Id` in errors, the signed
|
|
509
|
+
`message.status_changed` with the `client_reference`, and a WhatsApp form created, published, sent and completed
|
|
510
|
+
through the trigger. By hand, on 2026-10-16 with n8n 2.25.7 and the sandbox @ `7a1b3d8`: the credential test, a
|
|
511
|
+
workflow with "Create or update a contact" and "List tags", and the full trigger cycle.
|
|
512
|
+
|
|
513
|
+
## Regenerating
|
|
514
|
+
|
|
515
|
+
The node's operations are not written by hand: they come from `spec/funnelchat.json` with `npm run generate`. When
|
|
516
|
+
the portal publishes a new specification:
|
|
517
|
+
|
|
518
|
+
1. Copy the portal's unified specification (`out/specs/funnelchat.json`, built by its build) to
|
|
519
|
+
`spec/funnelchat.json`.
|
|
520
|
+
2. Write down in `spec/PROVENANCE.md` where it came from: branch, commit, date and SHA-256
|
|
521
|
+
(`shasum -a 256 spec/funnelchat.json`). The generator refuses to run if the SHA-256 is not there.
|
|
522
|
+
3. Run `npm run generate`. It updates `nodes/Funnelchat/generated/operations.ts` and this README's operations table,
|
|
523
|
+
and warns about what is worth checking (for example, a config tag that no longer exists).
|
|
524
|
+
4. Review the diff and run `npm run lint`, `npm run build` and `npm test`.
|
|
525
|
+
|
|
526
|
+
What changes on its own, without touching code: new operations, permissions, fields and pagination. What is set in
|
|
527
|
+
`scripts/`:
|
|
528
|
+
|
|
529
|
+
- **English texts.** The portal's specification is in Spanish. The name and purpose of each of its operations are in
|
|
530
|
+
`scripts/operations.en.json`: a new operation without an entry makes the generator stop and ask for one.
|
|
531
|
+
- **`scripts/generator.config.json`.** Which tags of the specification go into each resource, the English labels of
|
|
532
|
+
the fields (the ones not listed show their API name), `fieldDescriptions` to replace field descriptions of the
|
|
533
|
+
specification that are in Spanish or written for the domain's developers, and, in `operationNotes`, notices of what
|
|
534
|
+
a domain's code requires and its specification does not say. When regenerating, check whether those notices are
|
|
535
|
+
still needed.
|
|
536
|
+
- **Supplements.** `spec/supplements/` has what the specification does not have yet: the webhook subscriptions of
|
|
537
|
+
accounts and, from conversations `v1.18.10`, sending with an API key, reading a message and the forms. They are
|
|
538
|
+
written in English. If a new specification includes a route of a regular supplement, the generator uses the
|
|
539
|
+
specification's and says the supplement is no longer needed. A supplement with `"replaces": true` (the conversations
|
|
540
|
+
one) wins over the specification, with the same `operationId`, and the generator says so on every run: when the
|
|
541
|
+
specification brings it up to date, remove it from the config.
|
|
542
|
+
- **Event catalog.** `spec/webhook-events.json` is a copy of `GET /api/v1/webhooks/events` (the trigger's fallback
|
|
543
|
+
list). Replace it with a new copy (for example `snapshots/accounts/events.json` of the portal), write it down in
|
|
544
|
+
`spec/PROVENANCE.md`, add the English label of any new type to `scripts/events.en.json` and regenerate.
|
|
545
|
+
- **Watch out for `operationId` changes.** The value of each operation in n8n is its `operationId`. If the
|
|
546
|
+
specification changes it, saved workflows with the old operation stop finding it.
|
|
547
|
+
|
|
548
|
+
## Development
|
|
549
|
+
|
|
550
|
+
```bash
|
|
551
|
+
npm ci
|
|
552
|
+
npm run generate -- --check # the generated files are up to date with spec/
|
|
553
|
+
npm run lint # n8n's linter for community nodes (strict mode)
|
|
554
|
+
npm run build
|
|
555
|
+
npm test # vitest, without network or keys: simulated responses
|
|
556
|
+
FUNNELCHAT_SANDBOX_URL=http://127.0.0.1:4010 npm run test:e2e # e2e against a running sandbox
|
|
557
|
+
npm run dev # starts n8n on http://localhost:5678 with the node loaded
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
**Publishing:** push a `vX.Y.Z` tag on `main` matching the `package.json` version, and GitHub Actions
|
|
561
|
+
(`.github/workflows/publish.yml`) publishes it to npm with provenance.
|
|
562
|
+
|
|
563
|
+
- `nodes/Funnelchat/`: the action node. `generated/operations.ts` is the generated table; `description.ts` builds the
|
|
564
|
+
fields from it and `request.ts` builds the requests (route, query and body) and the pagination.
|
|
565
|
+
- `nodes/FunnelchatTrigger/`: the trigger. `generated/events.ts` is the built-in event list.
|
|
566
|
+
- `nodes/shared/`: the transport (key, base URL, errors, `429`) and the signature check.
|
|
567
|
+
- `test/`: the signature with the portal's vectors, building requests and pagination, the trigger's create, check and
|
|
568
|
+
delete cycle, and the custom base URL.
|
|
569
|
+
- `test/e2e/`: the node with real HTTP against the sandbox: send and read a message, file by URL, `X-Request-Id` in
|
|
570
|
+
errors, the trigger cycle with a really signed event, and a WhatsApp form end to end. Without
|
|
571
|
+
`FUNNELCHAT_SANDBOX_URL` it is skipped.
|
|
572
|
+
|
|
573
|
+
## Sources and what is left to verify
|
|
574
|
+
|
|
575
|
+
| What | Where it comes from | Status on 2026-10-22 |
|
|
576
|
+
| ------------------------------------------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
577
|
+
| The unified specification (most operations) | developers-portal, the same one the portal publishes (domain snapshots of 13-10) | Published; the portal regenerates it from the production tags |
|
|
578
|
+
| Sending with an API key, message by ID and forms | conversations tag `v1.18.10` (production), read from the code | Open to API keys. The new `operationId`s are not checked against a generated specification |
|
|
579
|
+
| Webhook subscriptions (trigger and Webhooks resource) | accounts tag `v1.4.24` (production), read from the code | Open to API keys with `webhooks:read` / `webhooks:write` |
|
|
580
|
+
| The trigger's fallback event catalog | accounts tag `v1.4.24`, extracted from the code | 54 types, 49 being emitted |
|
|
581
|
+
| The signature algorithm and vectors, the event envelope | developers-portal (`snippets/verificar-firma.js`, `tests/test_firmas.py`) | Published |
|
|
582
|
+
| The local sandbox of "Testing with the sandbox" | funnelchat-sandbox `feat/inicial` @ `7e244b8` | funnelchat-sandbox PR #1, not merged |
|
|
583
|
+
|
|
584
|
+
The details of each source are in [`spec/PROVENANCE.md`](spec/PROVENANCE.md).
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { IAuthenticateGeneric, Icon, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
|
|
2
|
+
export declare class FunnelchatApi implements ICredentialType {
|
|
3
|
+
name: string;
|
|
4
|
+
displayName: string;
|
|
5
|
+
icon: Icon;
|
|
6
|
+
documentationUrl: string;
|
|
7
|
+
properties: INodeProperties[];
|
|
8
|
+
authenticate: IAuthenticateGeneric;
|
|
9
|
+
test: ICredentialTestRequest;
|
|
10
|
+
}
|