@datarails-ai/dr-cli 0.0.1-locked → 1.6.70

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.
Files changed (58) hide show
  1. package/README.md +608 -0
  2. package/dist/chunk-DWMCBGQH.js +242 -0
  3. package/dist/cli.js +64468 -0
  4. package/dist/kb-6UWI7Q32.js +21 -0
  5. package/dist/scopes-map.json +4412 -0
  6. package/dist/skills/connector-sandbox/SKILL.md +95 -0
  7. package/dist/skills/datarails-connect-workato/SKILL.md +140 -0
  8. package/dist/skills/datarails-connectors-onboard-connector/SKILL.md +813 -0
  9. package/dist/skills/dr-cli/SKILL.md +659 -0
  10. package/dist/skills/dr-cli/reference/admin.md +796 -0
  11. package/dist/skills/dr-cli/reference/budget-builder.md +151 -0
  12. package/dist/skills/dr-cli/reference/calc-vals.md +73 -0
  13. package/dist/skills/dr-cli/reference/calcmodels.md +76 -0
  14. package/dist/skills/dr-cli/reference/cash.md +61 -0
  15. package/dist/skills/dr-cli/reference/dashboards-widgets.md +85 -0
  16. package/dist/skills/dr-cli/reference/datamappers.md +150 -0
  17. package/dist/skills/dr-cli/reference/desk.md +472 -0
  18. package/dist/skills/dr-cli/reference/filebox-documents.md +373 -0
  19. package/dist/skills/dr-cli/reference/forecast.md +51 -0
  20. package/dist/skills/dr-cli/reference/integrations.md +57 -0
  21. package/dist/skills/dr-cli/reference/lookups.md +168 -0
  22. package/dist/skills/dr-cli/reference/metrics.md +512 -0
  23. package/dist/skills/dr-cli/reference/multi-product.md +180 -0
  24. package/dist/skills/dr-cli/reference/reconciliation-mec.md +76 -0
  25. package/dist/skills/dr-cli/reference/rh-config.md +84 -0
  26. package/dist/skills/dr-cli/reference/safety.md +143 -0
  27. package/dist/skills/dr-cli/reference/spend-control.md +206 -0
  28. package/dist/skills/dr-cli/reference/templates.md +163 -0
  29. package/dist/skills/dr-cli/reference/workflows.md +137 -0
  30. package/dist/skills/dr-error-enrichment/SKILL.md +80 -0
  31. package/dist/skills/dr-error-enrichment/reference/core.md +257 -0
  32. package/dist/skills/dr-error-enrichment/reference/groundcover.md +84 -0
  33. package/dist/skills/dr-error-enrichment/reference/jira-bugs.md +219 -0
  34. package/dist/skills/dr-error-enrichment/reference/safety-backup.md +122 -0
  35. package/dist/skills/dr-error-enrichment-author/SKILL.md +219 -0
  36. package/dist/skills/dr-error-enrichment-improve/SKILL.md +218 -0
  37. package/dist/skills/dr-error-enrichment-maintain/SKILL.md +237 -0
  38. package/dist/skills/dr-error-enrichment-reconcile/SKILL.md +167 -0
  39. package/dist/skills/dr-filebox-api-setup/SKILL.md +170 -0
  40. package/dist/skills/dr-switch-user/SKILL.md +395 -0
  41. package/dist/skills/dr-validate/SKILL.md +392 -0
  42. package/dist/skills/dr-validate/report-template.html +950 -0
  43. package/dist/skills/fx-conversion-matrix/SKILL.md +763 -0
  44. package/dist/skills/fx-conversion-matrix/scripts/_args.mjs +52 -0
  45. package/dist/skills/fx-conversion-matrix/scripts/_xlsx.mjs +241 -0
  46. package/dist/skills/fx-conversion-matrix/scripts/build-matrix.mjs +366 -0
  47. package/dist/skills/fx-conversion-matrix/scripts/gen-mappers.mjs +171 -0
  48. package/dist/skills/fx-conversion-matrix/scripts/rates-to-xlsx.mjs +172 -0
  49. package/dist/skills/fx-onboard/SKILL.md +325 -0
  50. package/dist/skills/mec-table-validation/SKILL.md +383 -0
  51. package/dist/skills/plugins.json +24 -0
  52. package/dist/skills/validate-workato-migration/SKILL.md +167 -0
  53. package/dist/skills/validate-workato-migration/reference/gotchas.md +16 -0
  54. package/dist/skills/validate-workato-migration/reference/staging-orgs.md +26 -0
  55. package/dist/skills/validate-workato-migration/scripts/compare_files.py +255 -0
  56. package/dist/zendeskKb-JPSHVOPH.js +243 -0
  57. package/package.json +56 -9
  58. package/scripts/postinstall.mjs +91 -0
@@ -0,0 +1,659 @@
1
+ ---
2
+ name: dr-cli
3
+ description: Use when customer success or engineering needs to inspect OR change a customer's Datarails setup from the command line, or create/update/delete Datarails resources (templates, dashboards, widgets, functions, calculated values, dynamic ranges, datamappers, LUTs, fileboxes, integrations, users). Triggers - "create a chart/KPI/pie/table on this dashboard", "bulk-create users", "defer the invite emails", "change a user's login email", "turn email notifications on/off for a user", "re-enable emails for everyone in this org", "create N viewers with specific data permissions", "bulk-grant table/dashboard/filebox permissions from a CSV", "create many permission groups from a CSV", "upload this file", "compare Datarails values", "how many X does this customer have", "am I logged in", "who am I on this tenant", "is this the right customer?", "trigger a rescan", "look up a metric/KPI". Install/update/login → /dr-cli:install; a misbehaving CLI → /dr-cli:doctor; FX → /dr-cli:fx-conversion-matrix then /dr-cli:fx-onboard.
4
+ ---
5
+
6
+ # dr-cli — Datarails CLI
7
+
8
+ `dr` is a Node.js CLI for working with a customer's Datarails environment
9
+ (templates, dashboards, widgets, functions, calculated values, ranges,
10
+ datamappers, LUTs, fileboxes, integrations, metrics, reconciliation/MEC,
11
+ forecast, cash, spend, workflows, users, orgs) from any terminal on macOS or
12
+ Windows. Reads and writes are both supported.
13
+
14
+ This SKILL.md is the **router**: safety rules, auth, the full command map, the
15
+ core workflows, and the foot-guns that bite. Per-area detail lives in
16
+ `reference/*.md` — read the named file when you operate that area. Exact flags
17
+ for any command come from `dr <command> --help` (the binary is authoritative).
18
+
19
+ ## Safety rules (enforce these)
20
+
21
+ 1. **Prod writes need explicit in-chat confirmation.** ANY mutating command —
22
+ `create` / `update` / `delete` **and every other write path** (e.g.
23
+ `register-identity`, `bulk-create`, `bulk-set-email`, `bulk-permissions`,
24
+ `set-email`, `set-email-notifications`, `bulk-set-email-notifications`,
25
+ `set-password`, `set-field-type`) — on `US`, `US2`, `UK`, `CA`,
26
+ `EU`, `DEMO` (DEMO is a customer-facing prod tenant, not a sandbox) must be
27
+ confirmed by the user before you run them. `register-identity --confirm` in
28
+ particular sends real invite emails to customers, so it counts as a prod
29
+ write. Demonstrate on `DEV` / `DEV-1` / `TEST` / `TEST-1`..`TEST-7` first when
30
+ you can. Reads never need confirmation.
31
+ 2. **Every `delete` is a two-step confirm.** Running `… delete <id>` without a
32
+ token fetches the target, prints its name + key attributes, and emits a
33
+ short-lived token bound to `(resource, id, verb)`. Re-run with
34
+ `--confirm-token <tok>` to actually delete. `templates set-field-type` is
35
+ gated the same way (it can break widgets/ranges). Relay the target details to
36
+ the user first. See [reference/safety.md](reference/safety.md) for the
37
+ interactive prod gate and how the token works.
38
+ 3. **Prod destructives end in a desktop dialog, not a chat word.** After the
39
+ user's in-chat "yes, do it", re-invoke with the token — the binary pops a
40
+ dialog (or TTY prompt) where the *user* types a fresh challenge word; in a
41
+ headless shell it refuses non-zero. Don't add your own word ritual in chat.
42
+ Non-prod (`DEV`/`DEV-1`/`TEST`/`TEST-1`..`TEST-7`) is token-only, a plain
43
+ "yes" suffices; `DEMO` counts as prod. Details in
44
+ [reference/safety.md](reference/safety.md).
45
+ 4. **Credentials are sensitive.** Sessions live in the OS keychain, or in a
46
+ credential file where one is in use — never extract or echo JWTs or session
47
+ IDs into chat.
48
+ 5. **Don't invent commands.** If it isn't in the command map below, it doesn't
49
+ exist — escalate or ask. Never guess a flag; run `dr <command> --help`.
50
+ 6. **`UsageStats` is off-limits.** Never rescan/mutate it; `rescan table`
51
+ refuses it by name on every tenant.
52
+
53
+ ## Auth
54
+
55
+ Sessions live in the **OS keychain** (Windows Credential Manager / macOS
56
+ Keychain), keyed by `(server, email)` — or in a **credential file** where one is
57
+ in use (see below). With the keychain, multiple servers and multiple identities
58
+ per server can be logged in at once.
59
+
60
+ - `dr login --server US` — browser OAuth; session to the keychain, or to the
61
+ credential file when one is in use (see below).
62
+ - `dr login --api-key --server US` — save a user-bound **API key** instead of a
63
+ browser session. The key is read from a hidden prompt, or from stdin when
64
+ piped (`printf '%s\n' "$KEY" | dr login --api-key -s us`; piped, `-s` is
65
+ required unless a credential file already names the server; don't run the
66
+ piped form under shell tracing or command logging). Never pass the key as an
67
+ argument or paste it into chat. `dr` exchanges it for a JWT itself, so no
68
+ re-login is ever needed while the key is live. Keys come from an operator
69
+ today (no self-service command yet). `login-as` cannot run from an API-key
70
+ identity.
71
+ - `dr login-as <email> --server US` — impersonate a user, **JWT only, no
72
+ browser**. Needs an operator `login` first; stores a switchable identity you
73
+ then target with `--account <email>`. `--as <operator-email>` picks the
74
+ operator if several are stored; `--user-id <n>` skips the email lookup.
75
+ Refreshes/auto-remints silently — no re-prompt until the operator expires.
76
+ - `dr whoami --server US` — confirm who/where you are. Offline (decodes the local
77
+ JWT, no network). **Always sanity-check after login or a server switch.**
78
+ Shows `Impersonated by:` for a `login-as` identity, and a `Scope:` row for
79
+ every identity (see below).
80
+ - `dr logout --server US` / `dr logout --all`.
81
+
82
+ A **credential file wins over the keychain**: `dr` looks at
83
+ `DR_CREDENTIALS_FILE`, or `~/.dr/session.json` when that variable is unset, and
84
+ if the file is there every command reads its credentials from it — even on a
85
+ host whose keychain works — and writes it only when they change (`login`,
86
+ `logout`, a JWT refresh). Only when that one path is absent does `dr` use the
87
+ keychain. That is what makes `dr` usable where no keychain exists (CI, headless
88
+ hosts, containers). `dr whoami`'s `Auth:` line names the file in use. The file holds one
89
+ identity: `login` re-authenticates it and `logout` removes the file, but
90
+ `login-as` is refused (nowhere to put a second identity). If those credentials
91
+ are a bare token pair provisioned by something other than `dr login`, they
92
+ carry no session cookie and `login` / `logout` refuse over them: whatever
93
+ provisioned them owns renewing them. A file holding an `apiKey` is the CLI's
94
+ own to renew: `logout` deletes it and `login --api-key` replaces the key, while
95
+ a plain `login` is refused over it even when the file also holds a cookie,
96
+ because the key is what later commands authenticate as. `dr` warns on stderr,
97
+ once per identity, when a provisioner-supplied `apiKeyExpiresAt` is within
98
+ 14 days — from any command that authenticates and from `whoami`, which is where
99
+ an operator goes to inspect a credential; `login`, `logout` and `login-as` do
100
+ not show it. Nothing derives that date: the
101
+ exchange response does not carry it and `login --api-key` does not set it, so
102
+ update it in the file alongside the key, or the warning keeps naming the old
103
+ one.
104
+
105
+ `--server <KEY>` is **required on every command that hits an API**. Valid keys:
106
+ `US`, `US2`, `UK`, `CA`, `EU`, `DEV`, `DEV-1`, `TEST`, `TEST-1`, `TEST-2`, `TEST-3`, `TEST-4`, `TEST-5`, `TEST-6`, `TEST-7`, `DEMO` (case-insensitive).
107
+
108
+ You can also pass a full **`https://<host>.datarails.com` URL** instead of a
109
+ key to reach an ad-hoc / PR-preview environment, e.g.
110
+ `dr login --server https://<preview-host>.datarails.com`. Only `https`
111
+ and `*.datarails.com` hosts are accepted; the session is keyed by the hostname.
112
+ `dr logout --all` only revokes the built-in registry servers — log out of a URL
113
+ environment explicitly with `dr logout --server <url>`.
114
+
115
+ **Multi-identity:** when more than one account is stored for a server, pass
116
+ `--account <email>`. With one identity stored, `--account` can be omitted; a
117
+ missing `--account` against a multi-identity server errors with the available
118
+ emails.
119
+
120
+ ### Credential scope
121
+
122
+ An identity may carry a **scope** — a ceiling chosen by whoever approved the
123
+ credential (an agent's definition, an API key, a device-flow login), and it is a
124
+ ceiling on top of the user's own permissions, never a grant beyond them.
125
+ Commands outside it are hidden from `--help`, and invoking one anyway — or
126
+ hitting a server with it — exits **3** naming the `resource:action` that is
127
+ missing. **`dr whoami` prints the ceiling**: `Scope: Read only` (or `Read and
128
+ write` / `Full access`) for a preset, otherwise a `custom (N resources)` tree of
129
+ products, resources and the actions allowed on each, and
130
+ `Scope: unrestricted (no ceiling on this credential)` for a token that carries
131
+ no claim at all. Under `login-as` it reads `(inherited from operator)` — the
132
+ ceiling is the operator's. `dr whoami --json` adds `scopes` carrying the claim
133
+ exactly as the token holds it — an array, or a space-separated string, or `null`
134
+ when the token has no such claim. It is **not** normalised, so an entry `dr`
135
+ could not parse is still visible there; read the `Scope:` row for the ceiling
136
+ that is actually in force.
137
+
138
+ **Do not try to work around a scope** — no direct API calls, no switching
139
+ identity to dodge it. Report what you cannot do and the `resource:action` it
140
+ needs, so whoever minted the credential can widen it.
141
+
142
+ **`dr --help` can list less than the full tree, for two different reasons.**
143
+ The ROOT listing is context-dependent, and **a short listing never means "the
144
+ command does not exist."** Two mechanisms shorten it and they do not behave the
145
+ same:
146
+
147
+ 1. **No identity resolved** — no `--server` and no credential file, or a
148
+ `--server` whose stored identities `--account` did not narrow to one. Only
149
+ the few commands that run without an identity are listed (`login` among
150
+ them) and a line says why. Everything else is still registered, still runs,
151
+ and **still prints its own `--help`**; only the root listing changes. Re-run
152
+ as `dr --help --server <key>` — adding `--account <email>` when that server
153
+ holds more than one identity — to see the real tree.
154
+ 2. **A scope-restricted credential** — an identity DID resolve, and its token's
155
+ scope claim does not cover every command. The families it may not use are
156
+ hidden, and targeted help does **not** stay available: `dr <family> --help`
157
+ for a hidden one exits **3** with its own message naming the missing
158
+ `resource:action`. That is a credential limit, not a missing feature —
159
+ `dr whoami` shows the scope the identity carries.
160
+
161
+ Never tell a user `dr` cannot do something on the strength of a bare
162
+ `dr --help`.
163
+
164
+ **A server can refuse on scope too, and that also exits 3.** The hidden-command
165
+ rules above are this CLI's own map talking; the servers enforce the ceiling
166
+ themselves and answer `403` with a code:
167
+
168
+ - **`40301` — the credential's scope does not cover this call.** The message
169
+ reads `POST /api/…: this identity's credential scope does not allow it
170
+ (needs dashboards:write); run 'dr whoami' to see the scopes this
171
+ identity carries`. The scope is named from the local command map, so it is
172
+ absent for a command the map does not classify — the refusal is the same either way. **Do not retry, and do not
173
+ re-`login`**: a fresh token carries the same ceiling. Report the missing
174
+ `resource:action` to whoever minted the credential.
175
+ - **`40302` — the endpoint has no scope declared on the server yet.** The
176
+ message says `this endpoint is not yet scope-declared on the server; report
177
+ to #dr-cli`. Nothing about the credential is wrong; the CLI reached a route
178
+ the server's scope table does not know. Retrying cannot help — report it.
179
+
180
+ Neither goes through the JWT refresh ladder (a ceiling is not an expiry), so
181
+ neither can present as `session expired`. Any other `403` is an ordinary
182
+ permission / tenant / feature-flag refusal, still exits **1**, and is worth a
183
+ second look at `--server`, the org context, and the request body.
184
+
185
+ **JWT refresh is automatic** — the CLI refreshes before each request and on
186
+ 401/403, except on the two scope codes above, which no refresh can lift. Only
187
+ re-`login` when you see `session expired` as the *final* error after an
188
+ auto-refresh attempt. Do not re-login proactively on an expiry countdown.
189
+
190
+ **Connectors over VPN (TLS):** `dr connectors …` reaches the control-plane over
191
+ the corporate VPN. A TLS/certificate error there (`self signed certificate in
192
+ chain`, `unable to verify the first certificate`) means the VPN is inspecting
193
+ TLS — trust its gateway root CA via `NODE_EXTRA_CA_CERTS=<gateway-ca.pem>`,
194
+ which keeps verification on. Never set `NODE_TLS_REJECT_UNAUTHORIZED=0` (it also
195
+ exposes the monolith connection carrying the JWT). The CLI appends this fix to
196
+ the error inline; full steps in `dr connectors --help`.
197
+
198
+ ## Command map (don't improvise)
199
+
200
+ Every entry is a real subcommand. If it's not here, it doesn't exist. Names only
201
+ — run `dr <command> --help` for flags. `→` points to the reference file with that
202
+ area's gotchas.
203
+
204
+ **Session:** `login`, `whoami`, `logout`, `update` (self-upgrade the CLI).
205
+
206
+ **Templates / tables / ranges** → [reference/templates.md](reference/templates.md)
207
+ - `templates list|get|create|sample|distinct|aggregate|set-alias|set-field-alias|set-field-type`
208
+ - `set-alias` / `set-field-alias` set what the Datarails UI calls a table's or
209
+ field's **system name** — same value, different word. Read them with
210
+ `templates get <id> --json`.
211
+ - `data-models list|get`
212
+ - `dynamic-ranges list|get|create|update|delete|data`
213
+ - `scans status`, `rescan table`, `dataflow get`
214
+
215
+ **Datamappers** → [reference/datamappers.md](reference/datamappers.md)
216
+ - `datamappers list|get|update|delete|custom-functions|preview|bind|processed-versions|connected-fileboxes`
217
+ - `datamappers create flat|dimension|multiple|from-file`
218
+ - `mapper-recipes list|get|save`
219
+
220
+ **Calc models** → [reference/calcmodels.md](reference/calcmodels.md)
221
+ - `calcmodels list|get|create|update|set-flow|recalculate|delete` (alias `calc-models`)
222
+
223
+ **Calculated values (per-table)** → [reference/calc-vals.md](reference/calc-vals.md)
224
+ - `calc-vals list|get|create|update|delete` (alias `calcvals`) — `--table <id>` required on every subcommand
225
+
226
+ **Metrics (KPIs) & functions** → [reference/metrics.md](reference/metrics.md)
227
+ - `metrics list|get|data|drilldown`
228
+ - `metrics create base|calc`, `metrics update|delete`
229
+ - `metrics re-aggregate` — rebuild pre-aggregated metric values (targeted ids, or `--all` org-wide); add `--wait [--timeout <s>]` on targeted ids to block until done (exits non-zero on failure/timeout)
230
+ - `metrics migrate` — org-wide metrics+dimensions migration (V1→V2 backfill; `--dry-run` first)
231
+ - `metrics categories list|get|create|update|delete`
232
+ - `metrics dimensions list|get|create|update|delete|preview`
233
+ - `metrics suggestions list|refresh` + `metrics dimensions suggestions list|refresh` — AI suggestion agents (list shows suggestions + `last_run` status; `refresh` triggers a run, `--wait [--timeout <s>]` polls to completion). Gated per-org by Unleash flags; a successful `metrics migrate` auto-triggers both agents, so a 409 from `refresh` right after usually means it's already running — check `list`.
234
+ - `metrics suggestions profile` — read-only: profiles every table and returns, per table, the `reporting_axis` (WHICH date column a BASE metric should bucket by, with `chosen_by` naming the rule), `row_grain.reads_as` (`movements` -> time_aggregation SUM, `balances` -> EOP), candidate `measures`, `categoricals`, and the metrics already defined there. Use this BEFORE proposing any metric: `date_field` cannot be read off the payload (no field marks the reporting column) and guessing it produces a silently wrong number that passes validation. `reporting_axis: null` and `reads_as: "unknown"` are real answers - do not substitute a guess. All measure statistics are SAMPLED from storage-ordered windows and named `sampled_*`; there is deliberately no sum/avg. Needs no Unleash flag. ~30s and ~230 requests on a 43-table org.
235
+ - `metrics dimensions suggestions profile` — read-only: profiles every table/field and returns scored candidate dimension groups (value-overlap grouping, existing-dimension dedupe, machine-readable rejection reasons) in ONE call. Use this instead of looping `templates distinct` per field when reasoning about what dimensions an org should have. Needs no Unleash flag. `--json` for the payload, `--out <file>` when only `summary`/`groups` matter, `--suppression <file>` when you already hold the existing-dimension and dismissed lists. Also emits `near_misses` — pairs just under the merge threshold, for you to judge rather than silently dropped.
236
+ - `functions list|get|create|update|delete|set-filters` — a function's **definition** only.
237
+ - _`set-filters <id> --file <json> [--confirm]` is the **only** way to author a
238
+ function's row-level filters (create/update don't touch them). FULL REPLACE —
239
+ an empty array clears all; dimension `values` are a plain list, date filters
240
+ carry the add-in's opaque From/To blob. Dry-run unless `--confirm`._
241
+ To write a function's `DR.GET` into a raw `.xlsx`, that's `documents
242
+ add-functions` (see the Fileboxes/documents group), not a `functions`
243
+ subcommand.
244
+
245
+ **Dashboards, widgets, storyboards, reports, extables** → [reference/dashboards-widgets.md](reference/dashboards-widgets.md)
246
+ - `dashboards list|get|create|delete|prepare|set-date-picker`
247
+ - `dashboards push <id> --to <orgs> [--override]` — copy a dashboard into other
248
+ organizations (two-step confirm). Destinations must come from
249
+ `organizations push-targets`.
250
+ - `widgets list|get|data|create|update|delete|chart|kpi|pie|table|gauge|waterfall|published-item|text` (alias `widget`)
251
+ — `widgets data` applies the dashboard's saved filter bar + date picker by
252
+ default so results match the product UI; `--no-dashboard-context` computes
253
+ from the widget's own settings only.
254
+ - `storyboards list|get|slides|prepare|delete`
255
+ - `reports list|get|clone|delete`
256
+ - `extables list|get|clone|delete`
257
+
258
+ **Budget Builder (Budget management templates)** → [reference/budget-builder.md](reference/budget-builder.md)
259
+ - `budget-builder build|validate-row|add-row|save` — preview/build a budget
260
+ template definition, manage custom rows, persist to Budget management.
261
+ **Run the pre-flight questionnaire in the reference before building** —
262
+ scaffold-inherited values (actuals ending, fill mode, budget year) are the
263
+ #1 source of silently-wrong templates.
264
+ - `documents split create|list|status|undo` — split a saved budget file into
265
+ per-department child files (create/undo are two-step confirms).
266
+
267
+ **Fileboxes & documents** → [reference/filebox-documents.md](reference/filebox-documents.md)
268
+ - `filebox list|get|versions|tags|sheets|preview|create|upload|upload-version|update|download` (alias `fileboxes`)
269
+ - `filebox archive|archive-version|restore-version|set-version-include|set-version-date-tag|set-version-workflow|set-version-file-status|set-version-cycle`
270
+ - _archived versions (opt-in; defaults unchanged): `versions --archived|--all` to discover them; `archive-version`/`restore-version` also take `--doc` + `--version-number <n>` or `--latest-archived`/`--latest-active`_
271
+ - _include in database: `versions --included` lists the versions whose `include_file` flag is on (intent, not delivered rows); `set-version-include <vid> --include on|off` writes the flag (two-step confirm both ways — off deletes the version's table rows, on queues an ETL scan). `--doc <id> --all-except-latest` is the bulk cleanup; it never targets the latest, and warns in the plan if the latest is itself excluded_
272
+ - `filebox share|shares|set-option|set-tag-config|set-parent|lock|unlock|lock-date-tag|unlock-date-tag|locked-date-tags`
273
+ - `filebox get-auto-calc-settings|set-auto-calc-frequency|set-sync-frequency`
274
+ - `filebox push <id> --to <orgs> [--override]` — copy a filebox's latest version
275
+ and its dependent objects into other organizations (two-step confirm).
276
+ Destinations must come from `organizations push-targets`.
277
+ - `filebox data-query get|set|set-sync-interval|clear|sync` (alias `dq`)
278
+ - `filebox validation-rules options|set`
279
+ - `documents support-api set`, `documents calculate|sheets|debug-zip`
280
+ - `documents auto-calculate|add-functions|add-extables|add-dynamic-ranges` —
281
+ push a raw `.xlsx` through the server with **no filebox**: `auto-calculate`
282
+ recomputes it; `add-functions` injects Datarails functions' `DR.GET` formulas
283
+ into it by function id; `add-extables` registers ExTables into it by extable
284
+ id; `add-dynamic-ranges` registers Dynamic Ranges into it by range id. These
285
+ "add X to an Excel file" commands live under `documents`, **not** under
286
+ `functions`/`extables`/`dynamic-ranges`.
287
+
288
+ **Lookups (LUTs)** → [reference/lookups.md](reference/lookups.md)
289
+ - `lut list|list-by-doc|unmapped|data|create|create-table-lookup|update|update-table-lookup|delete|reorder|save-mappings|export` (alias `lookups`)
290
+ - Filebox vs table: **filebox lookups** use `create`/`update` (payload file; source is an uploaded file, keys in leading columns, **surfaces unmapped items** via `lut unmapped`). **table lookups** use `create-table-lookup`/`update-table-lookup` (flags — `--source-table`/`--keys`/`--fields`; source is another table, keys anywhere, **no unmapped-item surfacing**). Full decision guide in the reference.
291
+
292
+ **Integrations & company data sources** → [reference/integrations.md](reference/integrations.md)
293
+ - `integrations list|get|types|status|query-status|errors|sync|test-connection|add|update|delete|revoke`
294
+ - _`revoke <id>` is the OAuth **Disconnect** for a `DatarailsConnector` data source: it clears the
295
+ grant (`authorised=false`, `oauth` + `oauth_secrets` kept) and then revokes it at the vendor,
296
+ best-effort, when `oauth.revoke_url` is set. Two-step confirm (Tier A on prod); the output
297
+ names the vendor outcome (`revoked`, `rejected`, `not_supported`, ...). The customer must
298
+ reconnect before the next sync._
299
+ - `integrations share users|groups`
300
+ - `integrations it list|get|add|update|delete|resync` (IT-admin file-sync; redacts secrets by default)
301
+ - `entities list|qbd-availability|netsuite-connections|rename|rename-group|delete|mark-completed` (alias `companies`)
302
+ - `entities create qbo|xero|qbd|netsuite` (connected accounting systems)
303
+ - `error-enrichments list|stats|get|create|update|delete|test-regex|export|import` — the
304
+ regex→readable-message rules shown when a sync fails. `get`/`update`/`delete` take
305
+ one **or more** ids (`update` applies the same patch to each); `create` and
306
+ `update` also accept an **array** `--payload-file` (rule objects for `create`,
307
+ `{id, ...fields}` patches for `update`) for batch writes. Rules are **per server**;
308
+ `list`/`stats` take `--all-servers`, writes take one `--server`. `import` is
309
+ wipe-and-replace and zeroes hits/feedback counters — it is not the reconcile
310
+ tool: fleet changes are batch `create`/`update`, and `import` runs only when the
311
+ user names it and confirms the analytics loss and id reassignment. Start at the
312
+ **`dr-error-enrichment` skill** (router + shared reference on matching, safety,
313
+ and groundcover), then go straight to the task skill: **`dr-error-enrichment-author`**
314
+ to write/edit/delete rules, **`dr-error-enrichment-reconcile`** to diff and align
315
+ rules across servers, **`dr-error-enrichment-maintain`** to approve high-hit drafts
316
+ and fix thumbs-down rules, **`dr-error-enrichment-improve`** to mine groundcover
317
+ for unmatched errors and propose new rules.
318
+
319
+ **Reconciliation & MEC close-dates** → [reference/reconciliation-mec.md](reference/reconciliation-mec.md)
320
+ - `reconciliation accounts list|entities|entity-currencies|info|progress|settings|update-settings|set-assignee|clear-assignee|set-due-date|sign` (alias `recon`)
321
+ - `reconciliation processes dates|accounts`
322
+ - `reconciliation evidences list|get|process|supporting|file|create`
323
+ - `reconciliation filebox list|get|progress|create|delete|unlinked`
324
+ - `reconciliation audit status`
325
+ - `mec close-dates get|set`
326
+
327
+ **Workflows v2 (close & non-MEC) + collection processes** → [reference/workflows.md](reference/workflows.md)
328
+ - `workflow` and `mec workflow` share the same surface: `list|get|create|update|activate|deactivate|archive|restore|delete|clone|periods`, plus sub-groups `process`, `step`, `task`, `member`, `group` (`mec workflow` also has `accounts`)
329
+ - `collection-processes list|get|create|set-tag-config|add-fileboxes|add-folders|calculate` (alias `collection-process`)
330
+
331
+ **Forecast (Planning)** → [reference/forecast.md](reference/forecast.md)
332
+ - `forecast cycles list|get|documents|lock|unlock|set-default|roll-forward`
333
+ - `forecast scenarios list|get|versions|lock|unlock|set-default|delete|clone|clone-from-budget|bulk-edit|create`
334
+
335
+ **Cash** (own module) → [reference/cash.md](reference/cash.md)
336
+ - `cash currencies list|prefs|set-prefs`, `cash transactions`, `cash rescan-rules`, `cash set-default-currency`
337
+ - `cash rules list|get|create|update|delete|delete-all|reorder|import-csv|export-csv|ai-trigger`
338
+ - `cash tags list`, `cash hidden-rules upsert|delete`
339
+
340
+ **Spend Control** (own module) → [reference/spend-control.md](reference/spend-control.md)
341
+ - `spend-control billing-frequencies list`, `spend-control contracts list|get|extract-from-files|bulk-create|update|archive|restore|delete`, `spend-control files upload|list|get|delete`
342
+ - Attaching a document whose field values you already know? `spend-control files upload` returns file_ids with NO AI extraction cost; `extract-from-files` is for when you need the fields parsed.
343
+ - Need a contract UUID for `get`/`update`/`archive`/`delete`? `spend-control contracts list --all` is the only source.
344
+
345
+ **Multi-product (product-content catalog & per-org assignment)** → [reference/multi-product.md](reference/multi-product.md)
346
+ - `multi-product product-content list`
347
+ - `multi-product org-products list|set` — `set` has replace semantics; two-step confirm gated
348
+
349
+ **Legacy RH_CONFIG inspection (read-only)** → [reference/rh-config.md](reference/rh-config.md)
350
+ - `rh_config config|tables|download <bucket-id>` — inspect / download the bucket's config workbook (the Excel with the `RH_Config` sheet that the legacy parser reads).
351
+ - _`rh_config download` is broken (its endpoint rejects the JWT and it saves the login HTML page). Download instead with `filebox download <document> --doc-version <id>`, using `id` + `document` from `rh_config config <bucket-id> --json`._
352
+ - `rh_config doc-templates <doc-id> [--legacy-only]` / `rh_config version-templates <ver-id> [--v2] [--legacy-only]` — list TableTemplates with the `is_rh_config` flag surfaced.
353
+
354
+ **IT / admin / misc** → [reference/admin.md](reference/admin.md)
355
+ - `users list|get|can-login-as|me|set-password|set-email|set-email-notifications|bulk-create|bulk-set-email|bulk-set-email-notifications|register-identity|permissions|bulk-permissions`
356
+ - _`set-email <user_id> <new_email>` changes one user's **login email** via
357
+ `PUT /api/it/users/<id>` (the IT admin edit dialog's endpoint; preserves
358
+ `excel_online_email` and triggers the server's SSO email sync). **Dry-run by
359
+ default** — it GETs the user and prints `current -> new`; add `--confirm` to
360
+ apply. A target already in use errors (`email already in use`)._
361
+ - _`bulk-set-email --file <csv>` renames many users' login emails in one go
362
+ (CSV header `source,target`, one row per user). Same safety model as
363
+ `bulk-create`: resolves + validates EVERYTHING first — an unknown source,
364
+ sources spanning **more than one organization**, a target that collides with
365
+ an existing user, or a swap/chain all abort **before any write**. Dry-run
366
+ unless `--confirm`; on execute each row is isolated (a failure is recorded,
367
+ the batch continues, a non-empty failure set exits non-zero). Template:
368
+ `docs/examples/users-bulk-set-email.csv`._
369
+ - _`set-email-notifications <user_id> --enable|--disable` is the admin form's
370
+ **"Enable notifications"** toggle (`disable_emails`), written through the same
371
+ `PUT /api/it/users/<id>`. Exactly one of `--enable`/`--disable` required.
372
+ **Dry-run unless `--confirm`.** Refuses sync-service and support accounts
373
+ (provisioned notifications-off; the bulk verb skips them for the same
374
+ reason). Only `disable_emails` changes — the body
375
+ echoes the current `email` and `excel_online_email` verbatim so the server's
376
+ SSO email-sync never fires. **Blocked users:** the write lands, but reads
377
+ stay `off` until the user is unblocked (the server reports
378
+ `disable_emails OR is_blocked`) — the command says so; don't retry. To find
379
+ affected users, `users list` has an `EMAIL_NOTIF` column and
380
+ `--email-notifications on|off`._
381
+ - _`bulk-set-email-notifications --organization <id> --enable|--disable`
382
+ applies that toggle to a whole org (per-user writes — there is no org-level
383
+ notification setting). **Always skips** sync-service users, support users,
384
+ and users whose state the server didn't report; `--only-changed` also skips
385
+ users already in the target state, making re-runs cheap — but **never**
386
+ skips a blocked user, whose read state (`_disable_emails OR is_blocked`)
387
+ says nothing about the stored column. Aborts before any
388
+ write if the org matches no users. Dry-run unless `--confirm`; per-user
389
+ isolation on execute, failed ids named, non-zero exit._
390
+ - _`bulk-create --file <csv>` invites many users in one go, each scoped to
391
+ permission groups (the UI's "Invite Users → Set Permissions" flow, automated).
392
+ **Dry-run by default — add `--confirm` to actually create** (it sends real
393
+ invite emails). CSV header:
394
+ `email,first_name,last_name,role,permission_groups,department,job_role`
395
+ plus an **optional 8th column `register_identity`** (blank/absent ⇒ `true`;
396
+ set `false` to create that user WITHOUT identity-provider registration /
397
+ invite email — deferred setup). Blank `role` = VIEWER; `permission_groups`
398
+ are `;`-separated group **names**. Target org auto-resolves from the tenant's
399
+ permission groups, or pass `--organization <id>`. Existing emails aren't
400
+ recreated but ARE re-scoped (their listed groups get assigned), so re-runs
401
+ are idempotent and repair a prior partial failure. **`--no-register-identity`**
402
+ forces the deferred behavior for the whole batch; send the invites later with
403
+ `register-identity`._
404
+ - _`register-identity --file <csv>` sends the deferred invites for a
405
+ `bulk-create --no-register-identity` batch: feed it the SAME CSV (only `email` is
406
+ read), it resolves each email to its user and calls
407
+ `PUT /api/it/users/<id>/register_identity` to register + invite. Dry-run
408
+ unless `--confirm`; honest per-row status `registered`/`already_exists`/
409
+ `failed` (only `failed` exits non-zero, so re-runs are idempotent —
410
+ already-registered users report `already_exists`)._
411
+ - _`permissions <get|set-role|grant-table|revoke-table|grant-element|revoke-element|set-filters|remove-group>`
412
+ is the Permission Manager, one user at a time: data-table roles, element roles
413
+ (filebox/lookup/model/report/dashboard/storyboard/datasource), org role, and
414
+ row-level data-access filters. Requires the `use_new_table_perms` flag.
415
+ **Dry-run unless `--confirm`.**_
416
+ - _`bulk-permissions --file <csv>` applies many of those grants at once. Header:
417
+ `email,resource,type,ids,role,filter_values,filter_excluded` — one row per
418
+ grant, `resource` ∈ `table|element|role|filter`. Resolves everything first
419
+ and fails loud before any write (unknown email or table/element role aborts
420
+ the run); `filter` rows for a user aggregate into one full-replace. **Dry-run
421
+ unless `--confirm`.** Template: `docs/examples/users-bulk-permissions.csv`._
422
+ - `organizations list|get|add|contacts|permission-groups|push-targets|create-group|update-group|delete-group|bulk-groups` (alias `orgs`), `support-manager list`
423
+ - _`push-targets` lists the organizations this account may push a dashboard or filebox into — always run it before `dashboards push` / `filebox push`_
424
+ - _`create-group|update-group|delete-group` manage permission groups on the IT
425
+ app; `bulk-groups --file <csv>` provisions and augments many groups + their
426
+ membership from one CSV (header `group_name,role,members`). Dry-run unless
427
+ `--confirm`; `delete-group` uses the two-step confirm-token flow._
428
+ - `organizations export create|delete`, `organizations import run|delete-orgs` — full-org
429
+ migration between servers (export → email → import → email → export delete).
430
+ - _Both POSTs return immediately; **completion is email-only — there is no status
431
+ API to poll**. `import run` is permanent by default (`--temporary` opts into a
432
+ GC'd throwaway) and skips unknown model types by default (`--no-skip-models` to
433
+ fail instead). `import delete-orgs` **permanently deletes the whole organization**
434
+ (word-gated on prod). Renamed copies get RANDOMIZED user passwords (per-org
435
+ secret, recorded nowhere — use impersonation); real migrations pass
436
+ `--keep-user-passwords`. Full runbook: [reference/admin.md](reference/admin.md)._
437
+ - `devices list|get|logs|request-logs|received-logs|download-logs|commands|command-delete`
438
+ - `features list`, `features groups list|get`, `features orgs list|add|remove`
439
+ - `semantictables list|get|data|distinct` (alias `semtables`), `fx rates`
440
+ - `fx set-posting|set-reporting` (Monetary field FX roles; `--all-tables` sweeps the org), `fx default-currency` (org-wide default, Super Admin), `fx add-currency` (org custom currency, e.g. BUSD), `fx convert-functions|convert-widgets` (bulk currency-aware sweeps, `--off` to roll back)
441
+ - `kb search|get|categories|sections|articles-in-section|recent|attachments|article-images`
442
+ - `mcp register|unregister|list|serve`, `skill list|print|install|init-project`
443
+
444
+ **Desk** → [reference/desk.md](reference/desk.md)
445
+ - `desk teams list` — the org's Desk teams with their support emails; the only way
446
+ to get a `team_id` (no email-to-team lookup exists anywhere in the platform)
447
+ - `desk smtp set <team>` — store + verify a customer app password; `<team>` is an
448
+ id, a support email, or a team name (resolved against `teams list`)
449
+ - `desk smtp rotate <team>` — replace a stored app password in place when `set`
450
+ reports an existing ACTIVE connection; the row is never deactivated, so the
451
+ team keeps sending authenticated throughout
452
+ - `desk smtp get|test <team>` — inspect a stored connection (does it actually
453
+ route?) and re-test the credential without re-submitting it
454
+ - `desk support-email set <team> <email>` — register the team's
455
+ inbound address; do this BEFORE the customer creates their auto-forward rule
456
+ - `desk sendgrid register <domain>|status|verify` — SendGrid domain authentication
457
+ - `desk ticket create <team> --title … --requester-email … [--attach <path> …]` — open a
458
+ ticket; `<team>` resolves like `smtp set`'s. **Desk emails a confirmation to the
459
+ requester**, so it is confirm-gated and keeps the interactive word gate on production
460
+ - `desk clone --source-org <id> --target-org <id> [--dry-run]` — copy a reference
461
+ org's Desk (missing users with the e-mail domain swapped, missing teams + members,
462
+ config, tickets with history) into the token's org; match-or-create, safe to re-run.
463
+ Does NOT copy the org's `desk*` feature flags — `features orgs add` them first
464
+ - _Every `desk` verb is **org-scoped from the token** and takes no `org_id`: the
465
+ org is whatever `--server` + `--account` resolve to. Every one EXCEPT
466
+ `ticket create` is admin-only. `ticket create` instead needs Desk access — a
467
+ membership in some Desk team, or platform admin. Either way an integrations
468
+ engineer's own identity in a customer org is refused, so the working identity is
469
+ `dr login-as <customer-admin>` then `--account <that-email>`._
470
+ - _`--attach` is **create-then-upload**, not one call: the confirmation email goes out
471
+ before the files exist, a failed upload exits non-zero naming which files landed, and
472
+ the fix is to attach from the UI — re-running opens a SECOND ticket and re-emails the
473
+ requester. Extensions and the 25 MB cap are checked locally, before the create, and
474
+ `--json` becomes `{ticket, attachments}` (bare ticket when `--attach` is unused), so
475
+ read `.ticket.ticket_number` not `.ticket_number` when attaching._
476
+ - _The description is ALSO posted as the requester's first inbound message by default
477
+ (`--no-thread` opts out), because the UI's conversation pane renders the `messages`
478
+ table and not the `description` column - without it a CLI-created ticket looks empty.
479
+ Sends no email (`direction: inbound`). It does NOT clear the "requester not linked to
480
+ any record" banner - matching only runs in desk-ai's inbound handler; use
481
+ `--contact-id`._
482
+ - _`ticket create` may come back with a different team, status, priority or assignee
483
+ than you sent: an assignee forces `to_do`, an omitted `--due-date` is back-filled
484
+ from the computed SLA, and a routing rule can move the ticket to another team or
485
+ set the priority/assignee **for a flag you did not pass** (passing one suppresses
486
+ the matching rule). The command says so in its output — it is the server, not a
487
+ dropped flag._
488
+ - _`smtp set` reads the password from `--password-file` or `--password-stdin`
489
+ **only** — it is never a command-line argument. A `200` with `verified: false`
490
+ is the normal "customer gave us the wrong credential" outcome, not an error._
491
+
492
+ ## Core workflows
493
+
494
+ Add `--json` to any `list`/`get`/`data` when you'll parse the output; the default
495
+ table form is for humans. On Windows, pipe large `--json` to a file first
496
+ (`dr … --json > out.json`) — direct piping into `ConvertFrom-Json` can truncate.
497
+ **Read that file as UTF-8** — dr JSON carries non-ASCII and HTML-escaped entities
498
+ (`&amp;`, `&#x27;`, smart quotes); Python's default `cp1252` on Windows raises
499
+ `UnicodeDecodeError` mid-parse (`open(path, encoding="utf-8")`). And note some
500
+ `list` endpoints are **paginated** — `--json` returns a `{count,next,previous,results}`
501
+ envelope with only the first page, not the whole set (e.g. `features list`); pull
502
+ the rest with `--page`/`--page-size` rather than assuming one call is complete.
503
+
504
+ **KB-first when the user speaks product, not CLI.** If the request is a symptom
505
+ or how-to in product terms ("reconciliation isn't pairing", "how do I explain X
506
+ to the customer"), search the Help Center *before* acting: `dr kb search
507
+ "<query>"` (global content — no `--server`) or the `dr-zendesk-kb` MCP. It
508
+ returns the current article as markdown + URL — ground and cite your answer, then
509
+ map to the right `dr` command. Don't rely on memorized KB content; each reference
510
+ file also names the relevant KB folder for its area.
511
+
512
+ **Explore a tenant.** `dr whoami --server US` (confirm tenant) → `templates list`
513
+ → `dashboards list` → `functions list` → `metrics list`. Pick an ID and drill:
514
+ `templates get <id>` shows field names + IDs you'll need downstream.
515
+
516
+ **Peek at table data.** `templates sample <template_id> --rows 10 --server US`
517
+ (latest version, newest rows). `--fields "A,B"` projects columns; `--offset N`
518
+ pages; `--filter "Region=EU"` / `--filter-range "Period=2026-01..2026-12"`
519
+ narrow rows (dates as calendar dates or epoch seconds — see
520
+ reference/templates.md).
521
+
522
+ **Pull a function definition.** `functions get <id> --server US` returns the
523
+ *definition* — template, field, aggregator, and a **FILTERS** section (each
524
+ persisted filter's field/name/type/excluded/values), plus the default agg date
525
+ field; `--json` adds `filters` + `default_agg_*`. It is **not** the live value
526
+ (that's computed in Excel via `DR.GET`). A date filter's From/To lives inside
527
+ that filter's `values` — read it there instead of asking the customer.
528
+
529
+ **Pull raw rows from a dynamic range.** Ranges nest under a table:
530
+ `dynamic-ranges list --table <template_id>` → `dynamic-ranges data <range_id>
531
+ --table <template_id> --json`.
532
+
533
+ **Pull a KPI value.** `metrics list` → `metrics data --id <id> --scenario Actuals
534
+ --from 2026-03-01 --to 2026-03-31 --server US`. `metrics drilldown --id <id> …`
535
+ returns the source rows behind the number.
536
+
537
+ **Trigger a rescan.** `scans status <template_id>` → `rescan table <template_id>`
538
+ (two-step confirm: the first run prints the blast radius — document + version
539
+ counts — and a token; re-run with `--confirm-token` to fire it) → poll `scans
540
+ status` again. This is a **full historical** rescan (every calc field × every
541
+ version). To only pick up a datamapper change on the live template, prefer
542
+ `datamappers update <id> --rescan latest` (latest version only; mirrors "Publish
543
+ and scan") — far cheaper and lower-blast.
544
+
545
+ **Build a `create` body from plain-English intent** (calc models, datamappers,
546
+ metrics): you resolve names → integer IDs yourself against the tenant
547
+ (`templates list`, `templates get <id>`, `whoami`), construct the body, write it
548
+ to a tmpfile, then `… create --file <tmpfile>`. The operator never captures
549
+ network traffic or hand-edits JSON. Full recipes in the relevant reference file.
550
+
551
+ ## Foot-guns
552
+
553
+ - **"System name" is spelled `alias` here.** The Datarails UI, Help Center, and
554
+ most operators say **system name** for a table's or field's stable machine
555
+ identifier — the thing `DR.GET(value,"{department}")` resolves in `{braces}`,
556
+ and the thing template-library import maps on. dr-cli and the monolith API
557
+ both call it `alias`. If an operator asks for a system name, reach for
558
+ `templates set-alias` / `templates set-field-alias`, and read current values
559
+ with `templates get <id> --json` (the text table does not show them). Do not
560
+ report it as unsupported. Note the word is overloaded: command aliases
561
+ (`orgs` → `organizations`), `entities rename --alias` (a *display* name), and
562
+ filebox alias-matching are **not** system names. See reference/templates.md.
563
+ - **Metric value-field aggregators** are `SUM`, `COUNT`, `CUMULATIVE_SUM`,
564
+ `CUMULATIVE_COUNT` only — `AVG`/`MIN`/`MAX` are rejected. See reference/metrics.md.
565
+ - **CALC metric formulas:** each referenced metric name in `[brackets]`;
566
+ constants-only is rejected; reserved tokens `DAYS_IN_PERIOD` /
567
+ `PERIODS_IN_YEAR` are bare (not bracketed); time modifiers are
568
+ `[M].SHIFT(...)` / `[M].AS_OF(...)` — SHIFT unit `consumption_period`
569
+ follows the query grain; `PERIODS_IN_YEAR` metrics reject day-grain
570
+ queries. See reference/metrics.md.
571
+ - **`metrics data --agg`** is `DEFAULT|MTD|QTD|YTD|MTG|QTG|YTG`. The `*TG` "to
572
+ go" values accumulate **forward** from the current bucket (inclusive) to the
573
+ period end — the mirror of `*TD`. See reference/metrics.md.
574
+ - **Calc-model types** are `BEGINNING_BALANCE_Y_TO_M`, `BEGINNING_BALANCE_ONE_TO_M`
575
+ (`ONE_TO_M`, not `ONETIME_TO_M`), `CUMULATIVE_TO_M`. Self-loop models use
576
+ `DAILY`, not `AUTO`. See reference/calcmodels.md.
577
+ - **Calculated values are per-table** — `calc-vals` needs `--table <id>` on every
578
+ subcommand (incl. `get`/`delete`), and is distinct from calc models, functions,
579
+ and metrics. Flag-mode `update` leaves the value's FE "From" source untouched;
580
+ use `--file` to change it. See reference/calc-vals.md.
581
+ - **`templates set-field-type`** accepts `Text|Float|Date|Integer` and is
582
+ confirm-token gated.
583
+ - **Adding a calc column that references a LUT-injected column** (e.g.
584
+ `Amount = [Posting Amount] * [Sign]` where `[Sign]` comes from a lookup and
585
+ lands as **Text**): a plain `datamappers update … --rescan all` 500s
586
+ (`ETLError: … incorrect formula syntax`) because the server *infers* the LUT
587
+ column as Text and `Text * number` won't materialise. The UI "Publish and
588
+ scan" avoids this by sending `fields_to_change`. Replicate it from the CLI
589
+ with `datamappers update <id> --payload-file <p> --field <LUT_col>:Integer
590
+ --rescan all` (repeatable). Caveat: the server **rejects the type change with
591
+ HTTP 400** when the template has more than one mapper. See
592
+ reference/datamappers.md.
593
+ - **`datamappers create` needs the type up-front** — pick `flat`/`dimension`/
594
+ `multiple`/`from-file`. Use `--doc-version` (not `--version`, which collides
595
+ with the global `dr --version`).
596
+ - **Duplicating a mapper onto another table** — `datamappers get <mapper>
597
+ --table <src> --json` emits the source `template_id`; feed the (reshaped) body
598
+ to `datamappers create from-file <target>` and every `map_to` field type is
599
+ auto-resolved from the source template — no manual `--field` per field. It
600
+ **fails loudly** if any `map_to` target can't be resolved on the source rather
601
+ than mis-inferring it (DR-51801). Override with `--field NAME:TYPE`. See
602
+ reference/datamappers.md.
603
+ - **`--table` is required** on `dynamic-ranges list|data` and `lut list` — get the
604
+ template ID from `templates list` first.
605
+ - **`functions get`** returns the definition, never the live value.
606
+ - **Chart type** accepts either a composite literal (`column-stacked-percent`) or
607
+ flags (`--type column --stacked --percent`) — pick one.
608
+ - **`filebox data-query sync`** — scope to a period with `--date YYYY-MM-DD`
609
+ (e.g. `--date 2026-07-01`), not raw `--tag` JSON. The CLI truncates the
610
+ date to whatever granularity the filebox's own DATE tag actually uses —
611
+ no need to know if it's yearly/quarterly/monthly/daily/weekly. `--tag` is
612
+ an unvalidated escape hatch that forwards straight to the server; a
613
+ malformed value 500s with no error body. See reference/filebox-documents.md.
614
+ - **"User has no table permissions" is almost always a misread.** To see which
615
+ tables a user can access, use `dr users permissions get <id>` — never the raw
616
+ `GET /api/it/users/<id>` object's `permissions` field. That field is Django
617
+ **global capability flags** (`user.user_permissions`), empty for essentially
618
+ every VIEWER, and says nothing about data access; real table access lives in the
619
+ `user_to_check_role_id` sweep that `get` runs. See reference/admin.md.
620
+
621
+ ## Reference files
622
+
623
+ Read the matching file before operating that area; it carries the valid enums,
624
+ required-flag traps, and create-body recipes that `--help` doesn't spell out.
625
+ Each also names the relevant Help Center (KB) folder for customer-facing docs.
626
+
627
+ - [reference/safety.md](reference/safety.md) — confirm-token flow, interactive prod gate, escalation
628
+ - [reference/templates.md](reference/templates.md) — templates, fields/aliases, data-models, dynamic ranges, scans
629
+ - [reference/datamappers.md](reference/datamappers.md) — datamappers, calc-field formulas, recipes
630
+ - [reference/calcmodels.md](reference/calcmodels.md) — calc-model types, flow, create bodies
631
+ - [reference/calc-vals.md](reference/calc-vals.md) — per-table Calculated Values (formula refs, field/format semantics)
632
+ - [reference/metrics.md](reference/metrics.md) — metrics, categories, dimensions, functions
633
+ - [reference/dashboards-widgets.md](reference/dashboards-widgets.md) — dashboards, widgets, storyboards, reports, extables
634
+ - [reference/filebox-documents.md](reference/filebox-documents.md) — fileboxes, documents
635
+ - [reference/lookups.md](reference/lookups.md) — lookups (LUTs): filebox-sourced + table-sourced
636
+ - [reference/integrations.md](reference/integrations.md) — customer + IT-admin integrations, company data sources (entities)
637
+ - [reference/reconciliation-mec.md](reference/reconciliation-mec.md) — reconciliation, MEC close-dates
638
+ - [reference/workflows.md](reference/workflows.md) — workflow / mec workflow engine, collection processes
639
+ - [reference/forecast.md](reference/forecast.md) — forecast cycles & scenarios (Planning)
640
+ - [reference/cash.md](reference/cash.md) — Cash module
641
+ - [reference/spend-control.md](reference/spend-control.md) — Spend Control module
642
+ - [reference/multi-product.md](reference/multi-product.md) — product-content catalog + per-org assignment (replace-semantics PUT)
643
+ - [reference/desk.md](reference/desk.md) — Desk: outbound-email onboarding (SMTP app password + SendGrid domain auth) and ticket creation
644
+ - [reference/rh-config.md](reference/rh-config.md) — legacy RH_CONFIG inspection (read-only)
645
+ - [reference/admin.md](reference/admin.md) — users, orgs, devices, feature flags, semantic tables, fx, kb, mcp, skill
646
+
647
+ Everything else is discoverable: `dr --help` (families) → `dr <family> --help`
648
+ (subcommands) → `dr <family> <sub> --help` (flags). The binary is the source of
649
+ truth; `docs/reference.html` is a generated dump of all of it.
650
+
651
+ One catch on the first step: `dr --help` enumerates every family only when an
652
+ identity resolves AND that identity's credential scope covers them (see Auth).
653
+ A scope-restricted credential lists fewer families, and for a hidden one
654
+ `dr <family> --help` is refused with exit 3 naming the missing
655
+ `resource:action` — check `dr whoami` before concluding anything. Otherwise the short
656
+ listing is a missing identity: run it as `dr --help --server <key>`, plus
657
+ `--account <email>` where that server holds several identities, and the
658
+ per-family and per-command `--help` render in full either way. A short root
659
+ listing is never a missing command.