kenso 0.0.0-stage → 0.1.8
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 +74 -2
- package/dist/kenso.js +12220 -0
- package/dist/operations.json +1 -0
- package/docs/README.md +74 -0
- package/docs/agents.md +124 -0
- package/docs/auth.md +108 -0
- package/docs/batch.md +152 -0
- package/docs/crm.md +111 -0
- package/docs/leads.md +214 -0
- package/docs/output-and-errors.md +154 -0
- package/docs/reference.md +31729 -0
- package/docs/studio.md +113 -0
- package/package.json +46 -4
- package/skill/kenso-cli/SKILL.md +141 -0
package/docs/leads.md
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# Leads
|
|
2
|
+
|
|
3
|
+
Leads are contacts in a campaign. The lead commands live under `kenso contacts`, and `kenso leads` is the same thing: `kenso leads import` runs `kenso contacts import`.
|
|
4
|
+
|
|
5
|
+
| Command | What it does |
|
|
6
|
+
|---|---|
|
|
7
|
+
| `contacts import` | Add leads from a CSV or JSONL file to a campaign, with one result per row |
|
|
8
|
+
| `contacts update` | Edit leads in bulk from a file, matched by id or by email |
|
|
9
|
+
| `contacts remove` / `restore` | Remove (soft-delete) leads by id, and undo it |
|
|
10
|
+
| `contacts pause` / `resume` | Pause and resume the campaign sequence of leads (pause is beta, `pause_leads` flag) |
|
|
11
|
+
| `contacts tag` / `untag` | Add or remove a tag on leads by id |
|
|
12
|
+
| `contacts export` | Download a campaign's leads as one CSV file |
|
|
13
|
+
|
|
14
|
+
All of them except `export` are batch commands: without `--yes` they print a plan and change nothing. [Batch runs](batch.md) explains plans, run folders, results files and resume.
|
|
15
|
+
|
|
16
|
+
## Import a CSV into a campaign
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
kenso contacts import --template
|
|
20
|
+
kenso contacts import --campaign 812 --file leads.csv --dry-run
|
|
21
|
+
kenso contacts import --campaign 812 --file leads.csv --yes
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`--template` prints the accepted columns. A minimal file:
|
|
25
|
+
|
|
26
|
+
```csv
|
|
27
|
+
email,firstName,lastName,companyName,Pain Point
|
|
28
|
+
ann@acme.io,Ann,Lee,Acme,Slow onboarding
|
|
29
|
+
cid@globex.com,Cid,Moe,Globex,Churn
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Columns
|
|
33
|
+
|
|
34
|
+
Headers are matched without regard to case, spaces or punctuation. The known fields and the other headers each one reads:
|
|
35
|
+
|
|
36
|
+
| Field | Also read from | Rule |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| `email` | email address, e-mail, work email | Required, unless the campaign is LinkedIn-only and the row has a linkedin.com/in/ URL |
|
|
39
|
+
| `firstName` | first name, first, given name | Required |
|
|
40
|
+
| `lastName` | last name, last, surname, family name | Required |
|
|
41
|
+
| `title` | job title, position, role | Optional |
|
|
42
|
+
| `linkedInUrl` | linkedin, linkedin url, linkedin profile, profile url | Optional |
|
|
43
|
+
| `companyName` | company, organization, account, account name | `companyName` or `companyDomain` is required |
|
|
44
|
+
| `companyDomain` | website, domain, company website, url | `companyName` or `companyDomain` is required |
|
|
45
|
+
|
|
46
|
+
Every other column becomes a custom variable, named like its header in snake_case: a `Pain Point` column fills `{{pain_point}}` in your sequence. Names Kenso keeps for its own fields (`city`, `country`, `phone_number` and others) are not sent: the CLI warns and the plan lists them under `droppedColumns`. Rename them with `--map`.
|
|
47
|
+
|
|
48
|
+
### Cells
|
|
49
|
+
|
|
50
|
+
- A blank cell is never sent, so importing a file never erases data on a lead that is already in the campaign.
|
|
51
|
+
- The literal value `null` clears a custom variable.
|
|
52
|
+
|
|
53
|
+
### Rename or drop columns with --map
|
|
54
|
+
|
|
55
|
+
`--map` takes a JSON object, `@file` or `-` for stdin. A value names the target field, and `null` drops the column:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
kenso contacts import --campaign 812 --file leads.csv --map '{"Pain": "customVariables.pain_point", "Notes": null}' --yes
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### JSONL
|
|
62
|
+
|
|
63
|
+
Each line is one lead. Keys work like CSV headers, and a `customVariables` object is spread into custom variables:
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{"email":"ann@acme.io","firstName":"Ann","lastName":"Lee","companyName":"Acme","customVariables":{"pain_point":"Slow onboarding"}}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
kenso contacts import --campaign 812 --file leads.jsonl --yes
|
|
71
|
+
cat leads.jsonl | kenso contacts import --campaign 812 --file - --yes
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
A run read from stdin cannot be resumed with `kenso runs resume`, so prefer a file for large imports.
|
|
75
|
+
|
|
76
|
+
### What the CLI checks before sending
|
|
77
|
+
|
|
78
|
+
Every row is checked locally first. A row that fails goes to `invalid.csv` with one of these reasons and is not sent:
|
|
79
|
+
|
|
80
|
+
| Reason | Meaning |
|
|
81
|
+
|---|---|
|
|
82
|
+
| `missing_email` | The row has no email (and is not a LinkedIn-only lead) |
|
|
83
|
+
| `invalid_email` | The email is not a valid address |
|
|
84
|
+
| `missing_first_name` | The row has no first name |
|
|
85
|
+
| `missing_last_name` | The row has no last name |
|
|
86
|
+
| `missing_company` | The row has neither a company name nor a company domain |
|
|
87
|
+
| `sales_navigator_url` | An email-less row whose LinkedIn URL is a Sales Navigator link: add the email or a linkedin.com/in/ URL |
|
|
88
|
+
| `duplicate_in_file` | An earlier row of the file has the same email |
|
|
89
|
+
|
|
90
|
+
Kenso stays the authority and checks every row again.
|
|
91
|
+
|
|
92
|
+
### What the dry run shows
|
|
93
|
+
|
|
94
|
+
The dry run maps the columns, checks the rows, reads the campaign, and asks for a credit estimate (`credit-estimates create`, kind `leads`). Its plan holds `rows`, `valid`, `invalid`, `mapping`, `customVariableKeys`, `estimate`, `credits` (`needed`, `available`, `sufficient`) and `next`, the exact apply command. If the estimate call fails, the plan still prints with `"estimate":null` and an `estimateError`.
|
|
95
|
+
|
|
96
|
+
### How rows are sent
|
|
97
|
+
|
|
98
|
+
The CLI sends 1,000 rows per call to `POST /v1/campaigns/{campaignId}/contacts`, one call at a time per campaign, because two overlapping upserts of the same email could create duplicates. After a 5xx answer or a dropped connection it does not retry right away: it waits 130 seconds, so the first request has finished on the server, then resends the chunk once. Rows the first request saved come back `updated`.
|
|
99
|
+
|
|
100
|
+
### Reading the result
|
|
101
|
+
|
|
102
|
+
Each row in `results.csv` gets one status:
|
|
103
|
+
|
|
104
|
+
| Status | Meaning |
|
|
105
|
+
|---|---|
|
|
106
|
+
| `created` | A new lead in the campaign |
|
|
107
|
+
| `updated` | The lead was already there and was updated |
|
|
108
|
+
| `skipped` | Kenso kept the lead out on purpose. `reason` is `duplicate`, `blocklisted`, `already_contacted`, `excluded` or another code |
|
|
109
|
+
| `failed` | Kenso refused the row (`reason` such as `invalid_email` or `validation`, and an `error` message), or the call failed |
|
|
110
|
+
| `invalid` | The CLI refused the row before sending it (see the table above) |
|
|
111
|
+
|
|
112
|
+
A saved lead that cannot be emailed yet has `sendable` false, and the summary counts those leads in `notSendable` with a warning on stderr.
|
|
113
|
+
|
|
114
|
+
Exit codes: 0 when no row failed (skipped rows are not failures), 5 when some rows failed or were invalid, 6 when the plan's or campaign's lead limit stopped the run (`limit_reached`; raise the limit, then rerun the same command to send the rest).
|
|
115
|
+
|
|
116
|
+
## Import a very large file on the server
|
|
117
|
+
|
|
118
|
+
For up to 100,000 rows, `--server-import` uploads the CSV as one server import job and waits for it (default 30 minutes, `--timeout`). It reports the job's counts but no per-row results.
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
kenso contacts import --campaign 812 --file big.csv --server-import --yes
|
|
122
|
+
kenso contacts import --campaign 812 --file big.csv --server-import --timeout 1h --yes
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Update leads in bulk
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
kenso contacts update --file edits.csv --match id
|
|
129
|
+
kenso contacts update --file edits.csv --match id --yes
|
|
130
|
+
kenso contacts update --file edits.csv --match email --campaign 812 --yes
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
- `--match id` reads the contact id from an `id` or `contactId` column.
|
|
134
|
+
- `--match email` finds each email among the leads of `--campaign`. An email it cannot find is `not_found`. The email column only finds the lead; it is never sent as an edit.
|
|
135
|
+
- A column that is absent, or a blank cell, leaves that field unchanged. The literal `null` clears it.
|
|
136
|
+
- The lead fields you can edit are `firstName`, `lastName`, `title`, `linkedInUrl`, and `email` with `--match id`. `companyName` and `companyDomain` cannot change here and are not sent. Every other column is a custom variable, mapped the same way as in an import, and `--map` works here too.
|
|
137
|
+
- A row with nothing to change is `skipped` with reason `no_changes` and is not sent. Rows go 1,000 per call to `POST /v1/contacts/bulk-update`.
|
|
138
|
+
|
|
139
|
+
To edit one lead, use a one-row file from stdin:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
printf 'id,title\n9001,VP Sales\n' | kenso contacts update --file - --match id --yes
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Remove, restore, pause and resume
|
|
146
|
+
|
|
147
|
+
These four take contact ids from `--ids` (one id per line, or a CSV or JSONL file with an `id` or `contactId` column, or `-` for stdin), or from a search with `--where` (remove, pause and resume). `--where` takes the body of `POST /v1/contacts/search` as JSON, `@file` or `-`.
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
kenso contacts remove --ids ids.txt
|
|
151
|
+
kenso contacts remove --ids ids.txt --yes
|
|
152
|
+
kenso contacts remove --where '{"campaignIds": [812], "q": "acme"}' --yes
|
|
153
|
+
kenso contacts remove --ids big.csv --confirm 2500 --yes
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
- Without `--yes`, the plan prints the count and the first ids.
|
|
157
|
+
- Removing more than 1,000 contacts also needs `--confirm <count>`, and the count must match. The plan's `next` already carries the right `--confirm`.
|
|
158
|
+
- Each id comes back `ok`, `skipped` when there was nothing to do (for example `not_deleted` when you restore a contact that is not removed), `not_found` or `failed`.
|
|
159
|
+
- The remove summary carries the undo command, `"undo":"kenso contacts restore --run r_ubqu7wtx"`.
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
kenso contacts restore --run r_ubqu7wtx --yes
|
|
163
|
+
kenso contacts restore --ids ids.txt --yes
|
|
164
|
+
kenso contacts pause --where '{"campaignIds": [812]}' --yes
|
|
165
|
+
kenso contacts resume --ids ids.txt --yes
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
`restore` takes `--ids` or `--run <runId>` of an earlier remove, not `--where`: a search cannot find removed contacts. `pause` is a beta command for workspaces with the `pause_leads` flag.
|
|
169
|
+
|
|
170
|
+
## Tag and untag
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
kenso tags list
|
|
174
|
+
kenso contacts tag --tag 31 --ids ids.txt --yes
|
|
175
|
+
kenso contacts untag --tag 31 --ids ids.txt --yes
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
`tag` calls `POST /v1/tags/{tagId}/assignments` once per contact. A contact that already has the tag is `skipped` with reason `already_tagged`.
|
|
179
|
+
|
|
180
|
+
## Export a campaign's leads
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
kenso contacts export --campaign 812 --out contacts.csv
|
|
184
|
+
kenso contacts export --campaign 812 --wait
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
With `--out`, the CLI creates the export, waits for the job (default 30 minutes, `--timeout`) and writes one CSV file (mode 0600). Without `--out` it prints the export job; `--wait` waits for it, or run the `next` it prints, `kenso jobs wait <jobId> --type contact_export`.
|
|
188
|
+
|
|
189
|
+
## Recipes
|
|
190
|
+
|
|
191
|
+
**Tag every lead of a campaign.** List the ids, then tag them:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
kenso contacts list --campaign-id 812 --all -o ids > ids.txt
|
|
195
|
+
kenso contacts tag --tag 31 --ids ids.txt --yes
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**Fix the invalid rows and send them.** `invalid.csv` keeps each refused row's number and reason. Fix those rows in a new file and import it. Rerunning the original file also works: rows Kenso already saved come back `updated` or `skipped`.
|
|
199
|
+
|
|
200
|
+
**Retry only what failed.** Filter `results.csv` to the `failed` rows, take those rows from the input by `row` number, and import that file.
|
|
201
|
+
|
|
202
|
+
**Pause leads who replied elsewhere.** Search by filter, check the count, then apply:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
kenso contacts pause --where @replied.json
|
|
206
|
+
kenso contacts pause --where @replied.json --yes
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**Edit one field on many leads by email.**
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
kenso contacts update --file titles.csv --match email --campaign 812 --dry-run
|
|
213
|
+
kenso contacts update --file titles.csv --match email --campaign 812 --yes
|
|
214
|
+
```
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Output and errors
|
|
2
|
+
|
|
3
|
+
Every command follows one contract, so a script or an agent can parse any of them the same way.
|
|
4
|
+
|
|
5
|
+
## stdout and stderr
|
|
6
|
+
|
|
7
|
+
- **stdout carries data only.** Progress, warnings, notices and errors go to stderr.
|
|
8
|
+
- No colors, spinners or pagers. On a terminal you get readable tables; in a pipe you get JSON.
|
|
9
|
+
- The CLI never prompts. A batch command (an import, update, remove, tag or `--each` write) takes `--yes`; without it, the command plans or refuses. A single generated write, such as `kenso tags delete --tag-id 7`, and a write through `kenso api` send at once: add `--dry-run` to see the request first, then run the command without it.
|
|
10
|
+
|
|
11
|
+
## Formats
|
|
12
|
+
|
|
13
|
+
| What the command returns | stdout is a terminal | stdout is a pipe or file |
|
|
14
|
+
|---|---|---|
|
|
15
|
+
| One object | key-value lines | compact JSON on one line |
|
|
16
|
+
| A list | a table (first 8 scalar fields, cells cut at 60 characters) | NDJSON: one JSON object per line |
|
|
17
|
+
| A batch run | key-value lines | one JSON summary object |
|
|
18
|
+
| No content (HTTP 204) | nothing | nothing |
|
|
19
|
+
|
|
20
|
+
`-o` or `--output` picks the format: `json`, `ndjson`, `table` or `ids`.
|
|
21
|
+
|
|
22
|
+
- `-o json` on a list prints one JSON array. On a terminal, `-o json` pretty-prints an object.
|
|
23
|
+
- `-o ids` prints one id per line, ready for `--ids -`. Items without an `id` are skipped with one warning.
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
kenso campaigns list --limit 3
|
|
27
|
+
kenso campaigns list --limit 3 -o json
|
|
28
|
+
kenso contacts list --campaign-id 812 --all -o ids
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Shape the output
|
|
32
|
+
|
|
33
|
+
- `--fields a,b.c` keeps only those fields (dots reach into nested objects).
|
|
34
|
+
- `--query <JMESPath>` filters or reshapes in-process, so you need no `jq`. On a list it sees the whole list.
|
|
35
|
+
- Neither applies to a batch summary, which always prints in full.
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
kenso campaigns list --fields id,name,status
|
|
39
|
+
kenso campaigns list --all --query "[?status=='active'].id"
|
|
40
|
+
kenso me get --query "length(features)"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Lists and pages
|
|
44
|
+
|
|
45
|
+
A list command prints one page. When there are more, it prints the command for the next page on stderr, for example `next: kenso tags list --limit 1 --cursor MQ`. `--all` follows every page and streams the items. `--limit` sets the page size and `--cursor` starts at a cursor.
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
kenso tags list --limit 50
|
|
49
|
+
kenso tags list --all
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Errors
|
|
53
|
+
|
|
54
|
+
When stderr is not a terminal, an error is one JSON line on stderr:
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{"error":{"code":"unknown_flag","message":"Unknown flag --fiel for kenso contacts import.","field":"fiel","hint":"did you mean --file?","next":"kenso contacts import --help"}}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
| Key | Always there | Meaning |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| `code` | yes | A stable snake_case code to branch on |
|
|
63
|
+
| `message` | yes | One sentence for a person |
|
|
64
|
+
| `field` | no | The flag or input field at fault |
|
|
65
|
+
| `hint` | no | What to change |
|
|
66
|
+
| `next` | no | The exact command to run next, such as `kenso auth login`, `kenso workspace list --q <name>` or the same command with `--yes` |
|
|
67
|
+
| `httpStatus` | no | The API's HTTP status, when the API answered |
|
|
68
|
+
| `requestId` | no | The API's request id: quote it when you contact Kenso support |
|
|
69
|
+
|
|
70
|
+
On a terminal the same error prints as text: `Error: <message>`, then `field:`, `hint:`, `next:` and `requestId:` lines. The key never appears in an error.
|
|
71
|
+
|
|
72
|
+
### How API errors map
|
|
73
|
+
|
|
74
|
+
The CLI looks at the HTTP status first, then at the API's own `error.code`, which wins when present.
|
|
75
|
+
|
|
76
|
+
| HTTP status | Default code | Exit |
|
|
77
|
+
|---|---|---|
|
|
78
|
+
| 400, 422 | `validation_failed` | 2 |
|
|
79
|
+
| 401 | `unauthorized` (`next`: `kenso auth login`) | 3 |
|
|
80
|
+
| 403 | `forbidden`: the key's user or role cannot do this in this workspace | 3 |
|
|
81
|
+
| 404 | `not_found` | 4 |
|
|
82
|
+
| 409 | `conflict` | 7 |
|
|
83
|
+
| 429 | `rate_limited`, after the CLI waited and retried | 6 |
|
|
84
|
+
| 5xx | `server_error`, after retries where they are safe | 8 |
|
|
85
|
+
| anything else | `http_error` | 1 |
|
|
86
|
+
|
|
87
|
+
Two API codes change the exit code: `workspace_required` exits 2 with `"next":"kenso workspace list --q <name>"`, and `limit_reached` (the plan's or the campaign's lead limit) exits 6.
|
|
88
|
+
|
|
89
|
+
### Errors from the CLI itself
|
|
90
|
+
|
|
91
|
+
| Code | Exit | When |
|
|
92
|
+
|---|---|---|
|
|
93
|
+
| `usage_error` | 2 | A required flag or argument is missing, or two flags conflict (`--yes` with `--dry-run`) |
|
|
94
|
+
| `unknown_command` | 2 | No such command; `hint` suggests the closest one |
|
|
95
|
+
| `unknown_flag`, `invalid_flag`, `invalid_flag_value`, `unexpected_argument` | 2 | A flag or argument the command does not take |
|
|
96
|
+
| `invalid_query` | 2 | `--query` is not valid JMESPath |
|
|
97
|
+
| `file_unreadable` | 2 | An input file is missing or cannot be parsed |
|
|
98
|
+
| `config_invalid` | 2 | `~/.kenso/config.json` is not valid JSON: fix or delete it |
|
|
99
|
+
| `feature_not_enabled` | 2 | Every action of the resource is a beta command your workspace has not enabled |
|
|
100
|
+
| `invalid_key_format`, `key_required` | 2 | `auth login --with-key` read something that is not a Kenso key, or stdin is a terminal instead of a pipe |
|
|
101
|
+
| `invalid_workspace_id` | 2 | `kenso workspace use` got an id that is not a positive whole number |
|
|
102
|
+
| `unknown_topic` | 2 | `kenso docs` got a topic it does not have; `hint` lists the topics |
|
|
103
|
+
| `file_unwritable` | 2 or 1 | An output file cannot be written: `contacts export --out` (2), `skill install` (1) |
|
|
104
|
+
| `no_api_key` | 3 | No key in `KENSO_API_KEY` or the profile (`next`: `kenso auth login`) |
|
|
105
|
+
| `browser_login_unavailable`, `access_denied`, `login_expired` | 3 | Browser login is off, was denied, or timed out |
|
|
106
|
+
| `run_not_found` | 4 | `kenso runs show` or `resume` got an unknown run id |
|
|
107
|
+
| `docs_not_found` | 4 | `kenso docs reference` before the reference was generated (a source checkout without `pnpm build`) |
|
|
108
|
+
| `run_in_progress`, `already_filled` | 7 | Another process is working on the run, or `studio fill` already ran with this file and these flags |
|
|
109
|
+
| `skill_exists` | 7 | `kenso skill install` found a different `SKILL.md`; `next` adds `--force` to replace it |
|
|
110
|
+
| `job_timeout`, `job_failed`, `job_canceled` | 1 | `kenso jobs wait` (or a command that waits on a job) ran out of time, or the job failed or was canceled |
|
|
111
|
+
| `mcp_tool_error` | 1 | `kenso mcp call`: the MCP tool answered with an error |
|
|
112
|
+
| `unsafe_path` | 1 | `studio pull` refused a file path that would land outside `--dir` |
|
|
113
|
+
| `network_error` | 8 | The API could not be reached after retries |
|
|
114
|
+
| `upload_failed`, `download_failed` | 8 or 1 | A signed upload or download of a Studio file failed: 8 when storage was unreachable or answered 5xx, 1 on a 4xx (often an expired link: run the command again) |
|
|
115
|
+
| `pull_incomplete` | 8 | `studio pull`: a file came back with neither text nor a download link |
|
|
116
|
+
| `unexpected_response` | 1 or 8 | The API answered something the CLI could not read |
|
|
117
|
+
| `unexpected_error` | 1 | A bug: run again with `--verbose` and report it |
|
|
118
|
+
| `interrupted` | 130 | Ctrl-C |
|
|
119
|
+
|
|
120
|
+
## Exit codes
|
|
121
|
+
|
|
122
|
+
| Code | Meaning |
|
|
123
|
+
|---|---|
|
|
124
|
+
| 0 | OK, including a batch where every row succeeded or was skipped |
|
|
125
|
+
| 1 | Unexpected error |
|
|
126
|
+
| 2 | Usage or input error: a bad flag, a bad file, a validation error |
|
|
127
|
+
| 3 | Auth: no key, an invalid, revoked or expired key, or HTTP 401 or 403 |
|
|
128
|
+
| 4 | Not found |
|
|
129
|
+
| 5 | Partial: the batch finished and some rows failed, were not found or were invalid. See `results.csv` |
|
|
130
|
+
| 6 | Rate limited after retries, or a plan or campaign lead limit stopped the run (`limit_reached`) |
|
|
131
|
+
| 7 | Conflict or invalid state (HTTP 409), or a run another process holds |
|
|
132
|
+
| 8 | Server error after retries (5xx), or the API is unreachable |
|
|
133
|
+
| 130 | Interrupted. A batch run can be resumed: see its `next` |
|
|
134
|
+
|
|
135
|
+
Skipped rows (duplicates, blocklisted, already contacted) are not failures: a batch with only created, updated and skipped rows exits 0.
|
|
136
|
+
|
|
137
|
+
## Flags
|
|
138
|
+
|
|
139
|
+
- Command flags go after the command: `kenso contacts import --campaign 812`. Global flags can go anywhere.
|
|
140
|
+
- A boolean flag takes `--flag`, `--no-flag`, `--flag=true` and `--flag=false`.
|
|
141
|
+
- `--help` on any command prints its flags, arguments and examples. `kenso describe <resource> <action>` prints the same as JSON, with the body schema.
|
|
142
|
+
|
|
143
|
+
## Beta commands
|
|
144
|
+
|
|
145
|
+
Commands that need a workspace feature flag (CRM, Studio and others) are beta. Help and `kenso describe` hide the ones your workspace has not enabled and say how many they hid; `--all-commands` shows them. Calling a hidden command still works: Kenso decides. `kenso <resource>` on a resource whose every action is hidden answers `feature_not_enabled` (exit 2) and names the flags it needs.
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
kenso --help --all-commands
|
|
149
|
+
kenso describe --all-commands
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## What the CLI sends
|
|
153
|
+
|
|
154
|
+
Every API call carries `Authorization: Bearer <key>`, `Ken-Version: 1.1`, `X-Ken-Workspace-Id` (when a workspace is picked), `User-Agent: kenso-cli/<version> (node <version>; <platform>)` and `X-Ken-Client: Kenso CLI`. When a coding agent runs the CLI, `X-Ken-Client` names it (`Kenso CLI via Claude Code`) and writes carry `X-Ken-Actor: agent`, so Kenso's activity log shows who made a change.
|