@50heads/cli 2026.1009.1 → 2026.1013.1

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/CHANGELOG.md CHANGED
@@ -8,6 +8,24 @@ Pending changes are captured as changesets and written here when the package is
8
8
 
9
9
  ## [Unreleased]
10
10
 
11
+ ## [2026.1013.1] - 2026-10-13
12
+
13
+ **@50heads/cli 2026.1013.1 adds filing, organisation, webhook, answers export and approval flows, plus new status and limits commands.** It also changes login scopes for organisation admin access and updates wording and output to US English, with no change to commands or tools beyond the MCP server tool count.
14
+
15
+ ### Added
16
+
17
+ - **New filing commands: 50heads projects lists, creates, updates and deletes projects; 50heads labels lists labels in use; and 50heads file moves a question into a project, labels, archives or bookmarks it, or sets a reference once.**
18
+ - **50heads webhooks now lists, creates, updates, rotates, tests and deletes endpoints, lists deliveries and replays them, and can verify a captured delivery offline against Standard Webhooks and X-50heads-Signature.**
19
+ - **50heads answers --format jsonl now prints each matching answer as JSON lines, with choice and cohortHash, and --out can write it to a file.**
20
+ - **New organisation commands read domains, DNS records, SCIM provisioning, sessions, service accounts and audit events, with audit export to JSON lines or CSV.**
21
+ - **New status and limits commands show who the sign-in or key acts as, and the rate limits on the key and current usage in the window.**
22
+ - **Spend approvals are now supported in ask, including --wait-approval and 50heads approvals wait or status for an approval already asked.**
23
+
24
+ ### Changed
25
+
26
+ - **login now asks for organization:read, members:read and audit:read so an organization admin can use org and audit, and existing sign-ins and API keys keep their current scopes.**
27
+ - **Spelling is now US English throughout, including canceled, unauthorized, organization, authorized and enroll, and ask and estimate now print the answer count and price with no time estimate.**
28
+
11
29
  ## [2026.1009.1] - 2026-10-09
12
30
 
13
31
  **@50heads/cli 2026.1009.1 adds private asks for your own audience, including shared links, answer limits and set publishing from CSV.** It also updates wording and result output to show verified people, tier details and closed links, and adds more language options for ask.
@@ -56,3 +74,5 @@ Pending changes are captured as changesets and written here when the package is
56
74
  [2026.925.1]: https://github.com/50heads/cli/releases/tag/v2026.925.1
57
75
 
58
76
  [2026.1009.1]: https://github.com/50heads/cli/releases/tag/v2026.1009.1
77
+
78
+ [2026.1013.1]: https://github.com/50heads/cli/releases/tag/v2026.1013.1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # 50heads CLI
2
2
 
3
- Ask verified people from a script, a Makefile or CI, wait for the answers and read the split. 20 credits an answer at Tier 1, priced in your account's currency; fifty answers take about ten minutes.
3
+ Ask verified people from a script, a Makefile or CI, wait for the answers and see what they think. 20 credits an answer at Tier 1, priced in your account's currency.
4
4
 
5
5
  ```sh
6
6
  npx -y @50heads/cli --help
@@ -23,7 +23,8 @@ On your own machine you can sign in once in the browser instead:
23
23
 
24
24
  ```sh
25
25
  50heads login # kept in the macOS Keychain, the Secret Service, or ~/.config/50heads (mode 600)
26
- 50heads status
26
+ 50heads status # how this machine is signed in, and who that acts as
27
+ 50heads limits # the rate limits on this key, and how much of each is used
27
28
  50heads logout
28
29
  ```
29
30
 
@@ -34,7 +35,7 @@ The same sign-in works for `npx @50heads/mcp`. The API key wins when both are th
34
35
  ```sh
35
36
  # Price and time first. Never spends.
36
37
  50heads estimate --text "Which name sounds friendlier?" --option Pip --option Moss
37
- # 50 answers, about 10 minutes, $12.70. 20 credits an answer.
38
+ # 50 answers, $12.70. 20 credits an answer.
38
39
 
39
40
  # Ask and wait for the answers.
40
41
  50heads ask --text "Which name sounds friendlier?" --option Pip --option Moss --wait
@@ -63,6 +64,16 @@ The same sign-in works for `npx @50heads/mcp`. The API key wins when both are th
63
64
 
64
65
  Wrap it as `{"draft": {…}, "then": [{"text": "Why did you pick {winner}?"}]}` to add follow-ups.
65
66
 
67
+ ### Go or no go
68
+
69
+ On a yes/no question, `--go-above 60` asks for a verdict: a go when Yes is above 60% with enough answers, a no go when it is not, and unclear when there are too few answers or the range crosses the line. `--go-min-answers` sets the answers needed (default 80% of `--n`). The line is fixed once the question is live and costs nothing extra. The result prints the verdict first, as how these people answered against your line, not a fact.
70
+
71
+ ```sh
72
+ 50heads ask --text "Is this button label clear enough to ship?" --go-above 60 --wait
73
+ ```
74
+
75
+ Ranking and pairwise results also print who beats whom, and pairwise rows a 95% range on each win share.
76
+
66
77
  ### Many at once
67
78
 
68
79
  ```sh
@@ -70,7 +81,7 @@ Wrap it as `{"draft": {…}, "then": [{"text": "Why did you pick {winner}?"}]}`
70
81
  cat asks.jsonl | 50heads ask - --json
71
82
  ```
72
83
 
73
- CSV columns (a header row, any order): `text` (required), `type`, `options` (`A|B|C`) or `option1` to `option8`, `images` (`|`, in option order), `context`, `language`, `n`, `tier`, `rush`, `neither`, `mostly` (on a yes/no row, adds Mostly; otherwise the row is Yes or No), `countries` (`|`), `tags` (`|`), `stimulus_image`, `stimulus_text`. Every row is checked before anything is asked; a row that fails is reported and the rest go ahead (exit 8).
84
+ CSV columns (a header row, any order): `text` (required), `type`, `options` (`A|B|C`) or `option1` to `option8`, `images` (`|`, in option order), `context`, `language`, `n`, `tier`, `rush`, `neither`, `mostly` (on a yes/no row, adds Mostly; otherwise the row is Yes or No), `go_above` and `min_answers` (on a yes/no row, the go line as a whole percentage and the answers needed), `countries` (`|`), `tags` (`|`), `stimulus_image`, `stimulus_text`. Every row is checked before anything is asked; a row that fails is reported and the rest go ahead (exit 8).
74
85
 
75
86
  ### Never twice by accident
76
87
 
@@ -83,6 +94,7 @@ Every ask carries an idempotency key made from a hash of the question. Running t
83
94
  50heads results q_123 # the result so far
84
95
  50heads answers q_123 --option 0 --limit 100
85
96
  50heads answers q_123 --q price --all --json # one answer per line
97
+ 50heads answers q_123 --format jsonl --out q_123.jsonl # JSON lines, for scripts and model training
86
98
  50heads list --status live
87
99
  50heads cancel q_123 # stops it; unanswered heads are refunded
88
100
  50heads balance
@@ -91,6 +103,96 @@ Every ask carries an idempotency key made from a hash of the question. Running t
91
103
 
92
104
  `answers` filters by `--option` (0 is the first), `--tier`, `--country`, `--age-band` and `--q` (a word in written answers). Country and age filters show nothing when fewer than 5 answers match. Answers never identify a head.
93
105
 
106
+ `answers --format jsonl` prints every matching answer as JSON lines (one answer per line), from the API's JSONL export: the question, its options, the answer, `choice` (the answer as option indexes: `index`, `indexes`, `order` or `pair`) and `cohortHash`. `--out FILE` writes it to a file instead of stdout.
107
+
108
+ `results` ends with the cohort hash: the same value means the same kind of audience was asked (who, how many, which tier and targeting). It never identifies anyone.
109
+
110
+ ## File
111
+
112
+ ```sh
113
+ 50heads projects create "Spring menu" --description "Names and menus for the spring launch"
114
+ 50heads projects list # with question counts; --archived for archived ones
115
+ 50heads file q_123 --project prj_1 --label naming --label launch --bookmark
116
+ 50heads file q_123 --ref JIRA-42 # your own id, set once
117
+ 50heads labels # labels in use, with counts
118
+ 50heads list --project prj_1 --label naming
119
+ 50heads projects update prj_1 --archive
120
+ 50heads projects delete prj_1 --yes # the questions are kept, outside any project
121
+ ```
122
+
123
+ Filing costs nothing. A live question must be canceled before it is archived. A different `--ref` on a question that already has one is refused (exit 5).
124
+
125
+ ## Webhooks
126
+
127
+ Instead of polling, have 50heads send events to your own https address as they happen. The key or sign-in needs the `webhooks:read` and `webhooks:write` scopes; in a team, changes need the owner or an admin.
128
+
129
+ ```sh
130
+ 50heads webhooks create --url https://example.com/hooks/50heads \
131
+ --event question.completed --event question.underfilled --label launch
132
+ # whe_123 https://example.com/hooks/50heads on: question.completed, question.underfilled; only labels launch
133
+ # Signing secret (shown only now): whsec_…
134
+ 50heads webhooks test whe_123 # a signed webhook.test event now; exit 1 if it did not get a 2xx
135
+ 50heads webhooks list
136
+ 50heads webhooks update whe_123 --pause # --resume, --event, --url, --project, --label, --clear-filter
137
+ 50heads webhooks rotate whe_123 --overlap-hours 48
138
+ 50heads webhooks deliveries --failed
139
+ 50heads webhooks replay dlv_456
140
+ 50heads webhooks delete whe_123 --yes
141
+ 50heads webhooks events --type question.completed --limit 5 # the latest events, as deliveries carry them
142
+ ```
143
+
144
+ Events: `question.reserved`, `question.completed`, `question.underfilled`, `question.canceled`, `question.refused`, `credits.purchased`, `credits.low`, `credits.expiring`, `dispute.resolved`, `connection.cap_warning`, `approval.requested`, `approval.decided`, `approval.expired`, `approval.refused`, and for an organization's main team `member.provisioned`, `member.deprovisioned` and `policy.changed`. `--project` and `--label` limit question events to those projects or labels; other events always pass. The secret is printed once, on `create` and on `rotate`; after a rotation the old secret keeps signing for 24 hours unless you say otherwise (0 to 168), so the receiver can switch without missing a delivery.
145
+
146
+ Each delivery is signed twice: the Standard Webhooks headers (`webhook-id`, `webhook-timestamp`, `webhook-signature`), which the Standard Webhooks libraries verify with the secret as given, and `X-50heads-Signature`. To check a delivery you captured, with no network:
147
+
148
+ ```sh
149
+ FIFTYHEADS_WEBHOOK_SECRET=whsec_… 50heads webhooks verify --body-file body.json --headers-file headers.txt
150
+ ```
151
+
152
+ `--headers-file` takes `Name: value` lines (as `curl -D` writes them) or a JSON object. The body must be byte for byte as received. `--tolerance 300` also refuses a signature more than five minutes old, as a live receiver should. Exit 5 when it does not verify.
153
+
154
+ ## Slack and Zapier
155
+
156
+ The Slack workspaces 50heads is added to, and the Zapier accounts connected to it. Asks from either spend your account's credits, up to each one's daily cap. Add a workspace and change its cap in the portal, under Settings, then Integrations; Zapier connects from Zapier.
157
+
158
+ ```sh
159
+ 50heads apps list
160
+ # app_123 Slack · Acme 400 of 2,000 credits spent today added 2026-10-10 by Ana Price
161
+ # conn_456 Zapier 2 hooks 1,200 of 5,000 credits spent today connected 2026-10-09 by Ana Price
162
+ 50heads apps remove app_123 --yes # takes 50heads out of the workspace and revokes its Slack access
163
+ ```
164
+
165
+ `list` needs the `apps:read` scope; `remove` needs `apps:write` and, in a team, the owner or an admin. Questions already asked from Slack still finish in 50heads after a removal, but their results are not posted to Slack. A Zapier connection's hooks are its Zaps' triggers; remove the connection in the portal, under Settings, then Integrations, and its hooks go with it.
166
+
167
+ ## Organization (admins)
168
+
169
+ Read the organization of the team a key acts in. These commands never change anything; domains, sign-in rules, single sign-on, SCIM and service accounts are changed by a person in the portal (Settings, Organization). They need a key (or a sign-in) made for one of the organization's teams by an organization admin, or one of the organization's service accounts, with the scope shown.
170
+
171
+ ```sh
172
+ 50heads org show # organization:read: workspaces, domains, single sign-on, SCIM, sign-in rules
173
+ 50heads org domains # organization:read: each domain and the TXT record that proves it
174
+ 50heads org scim # organization:read: people and groups sent, tokens by label and prefix
175
+ 50heads org sessions usr_123 # members:read: where one person is signed in
176
+ 50heads org service-accounts # organization:read: scopes, caps, today's spend, last token
177
+ 50heads audit list --action domain --limit 50 # audit:read, newest first
178
+ 50heads audit export --from 2026-10-01 --out audit.jsonl # audit:read, oldest first, JSON lines (or --format csv)
179
+ ```
180
+
181
+ Each export is itself recorded in the audit log. Events never hold secrets, codes or anything that identifies a head.
182
+
183
+ ## Spend approvals
184
+
185
+ An organization can hold an agent's bigger asks until a person approves them. A held ask is not an error: the credits are held, nothing goes to heads, and `50heads ask` prints the approval and where a person decides, then exits 9.
186
+
187
+ ```sh
188
+ 50heads ask --text "Replace the paper menu with a QR menu?" --type yes_no --n 50 --wait-approval
189
+ # Waiting for approval apr_7QK2M9 … (exit 0 once approved and live; exit 1 if denied or expired)
190
+ 50heads approvals wait apr_7QK2M9 # wait for a decision on one you already asked
191
+ 50heads approvals status apr_7QK2M9 # read it once (exit 9 while pending)
192
+ ```
193
+
194
+ A denial prints `refused_by_policy` with the approver's role and their reason, never their name. Nobody deciding in time prints `approval_expired`; the credits go back either way. See [Spend approvals](https://50heads.com/help/teams/spend-approvals).
195
+
94
196
  ## Output and exit codes
95
197
 
96
198
  Human-readable by default. With `--json`, each result is one JSON object on stdout (JSON Lines for many), progress stays on stderr (`--quiet` turns it off), and errors are JSON on stderr: `{"error": {"code", "message", "exit"}}`.
@@ -102,10 +204,11 @@ Human-readable by default. With `--json`, each result is one JSON object on stdo
102
204
  | 2 | The command line is wrong |
103
205
  | 3 | Not signed in, or the key is not allowed to do that |
104
206
  | 4 | Not enough credits, or over the key's daily cap |
105
- | 5 | The question needs fixing (estimate prints the fixes) |
106
- | 6 | No question with that id |
207
+ | 5 | The question needs fixing (estimate prints the fixes), or a webhook delivery does not verify |
208
+ | 6 | No question, webhook endpoint or delivery with that id |
107
209
  | 7 | 50heads is unavailable or rate limited; try again |
108
210
  | 8 | Some asks in a bulk run failed; the rest were asked |
211
+ | 9 | The ask is held for a person to approve (spend approvals); `ask --wait-approval` waits for the decision instead, and a denial or expiry is exit 1 |
109
212
  | 124 | `wait` ran out of time and the question is still live |
110
213
 
111
214
  ## In CI
@@ -127,6 +230,7 @@ The key's daily cap is the limit on what a runaway job can spend.
127
230
  | `FIFTYHEADS_AUTH_URL` | Sign-in server for `login` |
128
231
  | `FIFTYHEADS_CREDENTIALS_STORE=file` | Keep the sign-in in a file, not the keychain |
129
232
  | `FIFTYHEADS_NO_BROWSER=1` | `login` prints the link without opening a browser |
233
+ | `FIFTYHEADS_WEBHOOK_SECRET` | The signing secret for `webhooks verify` |
130
234
 
131
235
  ## Versions
132
236