@funnelchat-hq/cli 0.1.1 → 0.2.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 +138 -15
- package/dist/cli.js +588 -132
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,8 +1,32 @@
|
|
|
1
1
|
# @funnelchat-hq/cli
|
|
2
2
|
|
|
3
|
-
The Funnelchat CLI
|
|
3
|
+
The Funnelchat CLI. **Connect a WhatsApp number, send a message and watch it get read**, in three commands, without writing code or a webhook:
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
+
npm install --global @funnelchat-hq/cli
|
|
7
|
+
|
|
8
|
+
funnelchat login # 1. paste your API key; it is not shown
|
|
9
|
+
funnelchat connect # 2. open the link it prints and scan the QR with WhatsApp
|
|
10
|
+
funnelchat send 5491122334455 "Hello from Funnelchat" # 3. and watch it: queued → sent → delivered → read
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
Sending to 5491122334455 from Support (5491100000002)…
|
|
15
|
+
queued
|
|
16
|
+
sent after 1 s
|
|
17
|
+
delivered after 2 s
|
|
18
|
+
read after 14 s
|
|
19
|
+
Message 98765 to 5491122334455: read 14 s after sending.
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The phone does not need to be a contact yet: the send creates it. Send it to your own phone to see it arrive.
|
|
23
|
+
|
|
24
|
+
**Handing it to an AI agent?** Agents are good at running commands. Two things help them: `funnelchat connect --no-wait` prints the link and ends at once (the agent shows it to you, and `funnelchat numbers list` says when the number is connected), and `--json` on any command gives one JSON document to read.
|
|
25
|
+
|
|
26
|
+
The same key also **follows your WhatsApp groups** (`funnelchat groups`), **exports your conversations and your contacts** to files you can verify, and checks your numbers, templates and MACs:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
funnelchat groups # the groups of your number, as WhatsApp has them
|
|
6
30
|
funnelchat contacts export --output-dir ./my-contacts
|
|
7
31
|
funnelchat conversations export --since 2026-10-01 --output-dir ./october
|
|
8
32
|
```
|
|
@@ -11,7 +35,8 @@ funnelchat conversations export --since 2026-10-01 --output-dir ./october
|
|
|
11
35
|
|
|
12
36
|
## What it does and what it does not
|
|
13
37
|
|
|
14
|
-
- **It only
|
|
38
|
+
- **It writes only when you ask it to.** `connect` creates a connect link, `send` sends a message (and creates the contact if the phone is new) and `groups follow` imports a group into your account. Every other command only reads: each call it makes is a `GET`.
|
|
39
|
+
- **A write is sent once.** The CLI never repeats it on its own; if the network fails halfway, it says the write may have arrived, so you can check before repeating it.
|
|
15
40
|
- **It exports by paging through the public API**, because the API has no server-side export. A large account takes a while: the CLI goes at a low pace on purpose, so it does not slow down your panel.
|
|
16
41
|
- **It never stores or shows your key** anywhere except your configuration file, if you use `funnelchat login`.
|
|
17
42
|
|
|
@@ -26,7 +51,7 @@ funnelchat --version
|
|
|
26
51
|
|
|
27
52
|
Or without installing it: `npx @funnelchat-hq/cli --version`.
|
|
28
53
|
|
|
29
|
-
###
|
|
54
|
+
### An export example
|
|
30
55
|
|
|
31
56
|
```bash
|
|
32
57
|
funnelchat login # paste your key; it is not shown
|
|
@@ -54,9 +79,15 @@ The CLI uses an API key of your account: `<id>|fc_live_...`, complete, as the **
|
|
|
54
79
|
|
|
55
80
|
| Command | Permission |
|
|
56
81
|
|---|---|
|
|
82
|
+
| `connect` | `accounts:operate` (WhatsApp numbers → Create, connect and delete) and `accounts:read` to wait for the number |
|
|
83
|
+
| `send` | `conversations:send`; to a phone that is not a contact yet, `conversations:write` (it creates the contact); `conversations:read` to watch the status; `accounts:read` to pick the number without `--device` |
|
|
84
|
+
| `groups` | `communities:read`; `accounts:read` without `--device` |
|
|
85
|
+
| `groups follow` | `communities:operate` (Groups and campaigns → Manage groups); `accounts:read` without `--device` |
|
|
57
86
|
| `contacts export`, `conversations export`, `templates`, `mac status` | `conversations:read` |
|
|
58
87
|
| `whoami`, `numbers` | `accounts:read` |
|
|
59
88
|
|
|
89
|
+
For the first use, a key with **WhatsApp numbers** (view and connect) and **Contacts and conversations** (view, edit and send) is enough.
|
|
90
|
+
|
|
60
91
|
There are two ways to give it the key:
|
|
61
92
|
|
|
62
93
|
**With an environment variable**, for scripts and servers:
|
|
@@ -105,6 +136,94 @@ FUNNELCHAT_API_KEY='1|fc_live_sandbox' funnelchat --base-url http://127.0.0.1:40
|
|
|
105
136
|
|
|
106
137
|
Every command takes `--json` for use from scripts and `--help` to see all its options.
|
|
107
138
|
|
|
139
|
+
### `funnelchat connect`
|
|
140
|
+
|
|
141
|
+
Connects a WhatsApp number to your account by QR code. It creates a connect link (`POST /api/v1/connect-links` with `type: "qr"`), prints it, and waits until the number is connected.
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
funnelchat connect
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
Open this link, tap the button and scan the QR code with WhatsApp on the phone you are connecting (Linked devices → Link a device):
|
|
149
|
+
|
|
150
|
+
https://…
|
|
151
|
+
|
|
152
|
+
It works once and expires at 2026-11-05T21:00:00+00:00; after tapping the button there are 10 minutes to scan the QR.
|
|
153
|
+
Whoever has the link can connect a number to your account: do not share it.
|
|
154
|
+
|
|
155
|
+
Waiting for the number to connect… (Ctrl+C to stop)
|
|
156
|
+
Connected: Support (5491100000002), id 4.
|
|
157
|
+
Send a first message: funnelchat send <phone> "Hello from Funnelchat"
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
| Option | What it does |
|
|
161
|
+
|---|---|
|
|
162
|
+
| `--no-wait` | Prints the link and ends, without waiting. For scripts and agents: `funnelchat numbers list` then shows when the number is connected. |
|
|
163
|
+
| `--timeout <seconds>` | How long to wait for the number. By default, 900 (15 minutes). |
|
|
164
|
+
|
|
165
|
+
- **The link** is a page on Funnelchat's domain. Opening it does not use it; tapping its button creates the number and shows the QR, which renews itself every 5 seconds. Treat it as a secret: it comes only in this answer, and whoever has it can connect a number to your account.
|
|
166
|
+
- **How it knows:** it lists your numbers before creating the link and then every 5 seconds (`GET /api/v1/devices`), and stops at the first one that was not connected before. Each listing counts toward the daily cap of accounts (about 180 calls in 15 minutes, of 10,000 a day).
|
|
167
|
+
- **With `--json`**, the link goes to stderr as soon as it exists, and stdout gets one document at the end, with the link and the number.
|
|
168
|
+
- **No QR in the terminal:** the page shows the QR that WhatsApp scans.
|
|
169
|
+
- If nobody scans the QR within 10 minutes of tapping the button, the link fails and the number it created is deleted: run `funnelchat connect` again. If the CLI stops waiting before (`--timeout`, exit code `8`, or Ctrl+C), the link still works until it expires.
|
|
170
|
+
|
|
171
|
+
### `funnelchat send <phone> <text>`
|
|
172
|
+
|
|
173
|
+
Sends a text message to a phone and shows its status live, until it is `read`, it `failed` or `--timeout` ends.
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
funnelchat send 5491122334455 "Hello from Funnelchat"
|
|
177
|
+
funnelchat send +54 9 11 2233-4455 "Hello" --device 4 --timeout 300
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
| Option | What it does |
|
|
181
|
+
|---|---|
|
|
182
|
+
| `--device <id>` | The number it sends from. By default, the only connected number of your account; with several, the CLI asks for this. |
|
|
183
|
+
| `--timeout <seconds>` | How long to watch the status. By default, 90. `0` sends and does not watch. |
|
|
184
|
+
|
|
185
|
+
- **The phone**, with its country code. Spaces, dashes, dots, parentheses and a leading `+` are dropped. It does not need to be a contact yet: the send creates it (`POST /api/v1/message/send-message`, with `device_id`, `phone` and `text`).
|
|
186
|
+
- **The status:** `queued` (in line, not sent yet), `sent`, `delivered` and `read`, or `failed` with the reason. The CLI reads it with `GET /api/v1/contacts/{contact}/messages/{message}`, 1 second after each change and then less and less often (2 s, 4 s… up to 10 s). If the recipient turned read receipts off, it stays `delivered`.
|
|
187
|
+
- **Exit codes:** `0` when it is read, or still on its way when `--timeout` ends; `6` when it failed (`code` `message_failed`, with the reason in `--json`). Ctrl+C stops watching (`130`); the message was already sent.
|
|
188
|
+
- **On an official number** (WhatsApp Cloud API), free text only goes out within 24 hours of the contact's last message, so the first message to a new contact has to be a template: the API answers `422 window_closed` and nothing is sent. A number connected by QR has no such window.
|
|
189
|
+
- **Rejections say what happened in plain words,** and that nothing was sent when the API guarantees it: `window_closed`, `recipient_mismatch`, `antiblock_*`, `device_unavailable`, `403` (the permission missing) and `429` (how long to wait). A send is never repeated on its own.
|
|
190
|
+
- **With `--json`**, one document with the message, every status with the seconds it took, and `outcome`: `read`, `timeout`, `unknown` (the provider never confirmed it: it will not change) or `not_watched`.
|
|
191
|
+
|
|
192
|
+
### `funnelchat groups`
|
|
193
|
+
|
|
194
|
+
The WhatsApp groups of a number connected by QR, as WhatsApp has them, and which ones you follow.
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
funnelchat groups
|
|
198
|
+
funnelchat groups --device 4
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
uid name followed
|
|
203
|
+
120363000000000001@g.us Launch Nov yes
|
|
204
|
+
120363000000000002@g.us Clients VIP no
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
- **Live:** `GET /api/v1/devices/{device}/groups` reads them from WhatsApp, and reuses the list for up to a minute per number: a group just created can take that long to show.
|
|
208
|
+
- **The number:** `--device`, or the only number of your account connected by QR.
|
|
209
|
+
|
|
210
|
+
### `funnelchat groups follow <group>`
|
|
211
|
+
|
|
212
|
+
Follows a group: imports it to your account, as importing it from the panel does. From then on, who joins and who leaves arrives as the `group_participant.joined` and `group_participant.left` webhook events.
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
funnelchat groups follow 120363000000000002@g.us
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
Following "Clients VIP" (120363000000000002@g.us).
|
|
220
|
+
Who joins and who leaves arrives as the group_participant.joined and group_participant.left webhook events.
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
- `<group>` is the `uid` that `funnelchat groups` shows. A group already followed says so, and nothing changes.
|
|
224
|
+
- **Only numbers connected by QR** (`422 provider_unsupported` otherwise), and up to 5 groups per number per minute (`429 import_rate_limited`). If the number is not an admin of the group, the CLI warns you: it can follow it, but not send messages to it or manage it.
|
|
225
|
+
- `POST /api/v1/groups/{group}/import` with `device_id`; it needs `communities:operate` (Groups and campaigns → Manage groups).
|
|
226
|
+
|
|
108
227
|
### `funnelchat whoami`
|
|
109
228
|
|
|
110
229
|
Which account the key belongs to, without showing the key.
|
|
@@ -269,8 +388,8 @@ MACs in the period: 120 of 1,000 (12 %) · 880 left
|
|
|
269
388
|
{
|
|
270
389
|
"manifest_version": 1,
|
|
271
390
|
"kind": "conversations",
|
|
272
|
-
"generator": { "name": "@funnelchat-hq/cli", "version": "0.
|
|
273
|
-
"api": { "base_url": "https://api.funnelchat.com", "spec": { "commit": "
|
|
391
|
+
"generator": { "name": "@funnelchat-hq/cli", "version": "0.2.0" },
|
|
392
|
+
"api": { "base_url": "https://api.funnelchat.com", "spec": { "commit": "99928866…", "sha256": "0ffaad2…" }, "routes": [ … ] },
|
|
274
393
|
"started_at": "2026-10-16T12:00:00.000Z",
|
|
275
394
|
"finished_at": "2026-10-16T12:41:10.000Z",
|
|
276
395
|
"runs": 2,
|
|
@@ -318,8 +437,9 @@ funnelchat contacts export --output-dir ./contacts --resume
|
|
|
318
437
|
|
|
319
438
|
- **Low pace:** at most 1 call per second, retries included. Change it with `--rate` (greater than 0 and up to 10; `0.5` is one every two seconds) or `FUNNELCHAT_RATE`. The API's per-minute limits are counted per user and your panel shares them: raise it carefully.
|
|
320
439
|
- **`429` and `503`:** the CLI waits exactly what `Retry-After` says and retries. If it asks for more than 60 seconds (`--max-wait` changes that), it stops with code `5` and tells you when it resets.
|
|
321
|
-
- **Server and network errors:** it retries with exponential backoff (1 s, 2 s, 4 s… up to 30 s), up to 4 times; also a `502 origin_unreachable` from the API's entry point. **It only retries reads
|
|
322
|
-
- **
|
|
440
|
+
- **Server and network errors:** it retries with exponential backoff (1 s, 2 s, 4 s… up to 30 s), up to 4 times; also a `502 origin_unreachable` from the API's entry point. **It only retries reads**: a write (`connect`, `send`, `groups follow`) is sent once, and a `429` on it says how long to wait. A `500 edge_misconfigured` is not retried: it is a configuration problem on our side and waiting does not fix it.
|
|
441
|
+
- **Waiting without hurry:** `connect` lists your numbers every 5 seconds; `send` reads the message 1 second after each change and then less and less often, up to every 10 seconds.
|
|
442
|
+
- **The daily cap:** accounts and communities allow up to 10,000 calls per day per key, each on its own; conversations has no daily cap. The contacts and conversations exports and the message of `send` use only conversations; `whoami`, `numbers`, `connect` and picking the number without `--device` use accounts; `groups` uses communities. If the cap is reached, the CLI stops with a clear message and the reset time, and the export can be resumed.
|
|
323
443
|
|
|
324
444
|
## For scripts
|
|
325
445
|
|
|
@@ -345,8 +465,9 @@ The CLI asks the API for its messages in Spanish (`Accept-Language: es`), so the
|
|
|
345
465
|
| `3` | No key, a key the API rejects (`401`) or a key without the permission the route needs (`403`). |
|
|
346
466
|
| `4` | Not found in your account (`404`), or the route does not exist in the API (`404 route_not_found`). |
|
|
347
467
|
| `5` | An API limit the CLI does not wait for on its own: the daily cap or a long `Retry-After`. It can be resumed. |
|
|
348
|
-
| `6` | The API failed (`5xx`, `422`…) or could not be reached, after the retries. |
|
|
468
|
+
| `6` | The API failed (`5xx`, `422`…) or could not be reached, after the retries; or the message `send` watched failed. |
|
|
349
469
|
| `7` | The output already exists: the CLI never overwrites a file. |
|
|
470
|
+
| `8` | `connect` waited `--timeout` and no number connected. |
|
|
350
471
|
| `130` | Interrupted with Ctrl+C. An export can be resumed. |
|
|
351
472
|
|
|
352
473
|
## What the API does not allow yet
|
|
@@ -368,7 +489,7 @@ The CLI uses only routes that exist in the public spec (`spec/routes-used.json`
|
|
|
368
489
|
|
|
369
490
|
## Where the spec comes from
|
|
370
491
|
|
|
371
|
-
`spec/funnelchat.json` is the portal's unified
|
|
492
|
+
`spec/funnelchat.json` is the portal's full unified spec (developers-portal `main` @ `99928866`, deployed as v0.6.3 on 2026-11-05: `generated/specs/funnelchat-completa.json`, which carries every operation, not only the ones the portal shows), built from the snapshots of accounts `v1.5.97` (`558332bcf`), conversations `v1.19.21` (`f2937a713`) and communities `54643bbd` (the app code of `v2.35.7`). `spec/procedencia.json` says so, with the file's `sha256`. Every route, parameter and body field the CLI uses is in it: `spec/pending.json` stays empty.
|
|
372
493
|
|
|
373
494
|
## Development
|
|
374
495
|
|
|
@@ -381,7 +502,7 @@ npm run build
|
|
|
381
502
|
|
|
382
503
|
To develop you need Node 20 or later (for the test tools); the CLI it builds runs on Node 18.
|
|
383
504
|
|
|
384
|
-
**The tests** use simulated answers, with the same shapes as the API (pages by number and by cursor). They cover paging, resuming, `429`s, that the key never shows up in any output or any file, that no file is overwritten,
|
|
505
|
+
**The tests** use simulated answers, with the same shapes as the API (pages by number and by cursor). They cover paging, resuming, `429`s, that the key never shows up in any output or any file, that no file is overwritten, the manifest's SHA-256, and the first use: `connect` waiting for the number, and `send` watching the status until it is read.
|
|
385
506
|
|
|
386
507
|
**End to end against the sandbox.** With an already built checkout of [funnelchat-sandbox](https://github.com/Funnelchat20/funnelchat-sandbox) (`npm install && npm run build` in its folder), this test starts it in the same process, on a free port, and runs the CLI against it. It does not touch the sandbox's repository:
|
|
387
508
|
|
|
@@ -391,17 +512,19 @@ FUNNELCHAT_SANDBOX_DIR=../funnelchat-sandbox npm run test:e2e
|
|
|
391
512
|
|
|
392
513
|
Without that variable, `npm test` skips it.
|
|
393
514
|
|
|
394
|
-
**The spec.** It is copied and regenerated with a single script, which records where it came from in `spec/procedencia.json` and checks that every route and every
|
|
515
|
+
**The spec.** It is copied and regenerated with a single script, which records where it came from in `spec/procedencia.json` and checks that every route, every parameter and every body field the CLI uses exists:
|
|
395
516
|
|
|
396
517
|
```bash
|
|
397
|
-
npm run generate -- --spec ../developers-portal/
|
|
398
|
-
npm run generate
|
|
399
|
-
npm run generate:check
|
|
518
|
+
npm run generate -- --spec ../developers-portal/generated/specs/funnelchat-completa.json # a new spec
|
|
519
|
+
npm run generate # regenerate
|
|
520
|
+
npm run generate:check # is it up to date?
|
|
400
521
|
```
|
|
401
522
|
|
|
523
|
+
The portal's full spec is a build output git ignores, so a copy of it says where it came from on the command line, as funnelchat-node does: `--expect-sha256 <hex>`, `--source repository=… --source branch=… --source commit=… --source path=…` and `--domain <area>=<sha>@<branch>@<snapshot_at>` for each area. `spec/procedencia.json` marks those fields as declared.
|
|
524
|
+
|
|
402
525
|
`spec/pending.json` is for a route that is not in the snapshot yet; today it is empty. When a new snapshot has a route from there, the generation says it can be deleted.
|
|
403
526
|
|
|
404
|
-
**Publishing.** Push a `vX.Y.Z` tag on `main` that matches the version in `package.json
|
|
527
|
+
**Publishing.** Push a `vX.Y.Z` tag on `main` that matches the version in `package.json`; GitHub Actions publishes it through npm Trusted Publishing (`.github/workflows/publish.yml`).
|
|
405
528
|
|
|
406
529
|
## License
|
|
407
530
|
|