CLI
pip install d2b-sdk (PyPI, source on GitHub) also installs the d2b command (under pyenv, prefer pipx install d2b-sdk / uvx --from d2b-sdk d2b — it avoids shim-resolution accidents). Output is JSON (stdout); errors carry the suggested_fix on stderr; exit codes are 0 / 1 (API errors, refused syncs) / 2 (usage). Nothing prompts interactively.
Authentication
Section titled “Authentication”Browser login is the default (no raw API keys to handle):
d2b login # defaults to https://d2b.dev (--base-url / $D2B_BASE_URL for another deployment)# → a confirmation code and URL appear and the browser opens. Check the code on# screen matches the terminal, tick the accounts this CLI may act for, then# approve. One token is issued per account and stored at# ~/.config/d2b/credentials.json (0600).# CLI tokens live 90 days — just `d2b login` again when they expire.d2b login --scopes workbooks:read,workbooks:write# Default is the whole workbooks family — read + write + delete — so the CLI# can delete the workbooks it creates; pass --scopes only to narrow (e.g. read-only).d2b whoami # includes account_id / account_name / workspace_named2b workspaces list # the workspaces this credential reaches, by name (is_default = where creates land)d2b --account acc-… whoami # switch accounts when several were approved ($D2B_ACCOUNT_ID works too)d2b logout # forgets the saved login and revokes every server-side tokenA token is always bound to exactly one account (one token = one account). The approval page pre-selects your default account; each developer account you add gets its own token. Without --account the default account’s token is used.
A login token reaches resource=account: the whole account it is bound to — for the default account, your personal workspace plus the team workspaces you are an active member of; for a developer account, every workspace that account funds. d2b workbooks create --workspace-id … can create in any of them, and the response’s workspace_name says where it landed. Reach is set by that pin alone; scopes say what the token may do (workspaces:read is the control plane’s permission to read workspace settings and does not change reach). Naming a workspace the token does not reach in --workspace-id is a 403 for listing and creation alike (d2b workspaces list shows the reach). When you need a credential confined to one workspace, mint a key with resource: "workspace:<id>" in the console or via POST /api/v1/me/tokens and use it through D2B_API_KEY.
Non-interactive environments (CI, agents) use environment variables (precedence: flags > env > saved login). Never pass API keys as command-line arguments (--api-key is rejected, and secret-shaped strings are redacted from parser errors).
export D2B_API_KEY=d2b_pat_... D2B_BASE_URL=https://d2b.devThe main commands
Section titled “The main commands”d2b workbooks create --title monthly # → {"id": "..."}d2b workbooks listd2b upload sales.xlsx --workbook WB --wait # async ingest + job waitd2b tables list --workbook WBd2b tables schema sales --workbook WB --json-schemad2b tables rows sales --workbook WB --limit 50d2b tables a1 sales A1:D10 --workbook WB # read in Excel coordinatesd2b tables write-a1 sales B2:C3 '[[10],[20]]' --workbook WB --expected-version 12d2b tables add-column sales with_tax --type DOUBLE --workbook WBd2b tables set-formula equipment utilization "{units_active} / {units_total}" --workbook WBd2b query 'SELECT count(*) FROM "sales"' --workbook WBd2b review --workbook WB --table sales # evidence-backed findings (--agent: verified, with a summary)d2b export --workbook WB --format xlsx -o out.xlsxd2b sources render report.xlsx --workbook WB -o monthly.xlsx # original formattingd2b sources revise equipment.xlsx --workbook WB --transform-name merge_sites --range A3:N8 --sql-file merge.sql -o out.xlsxd2b sheets list --workbook WBd2b sheets put report --spec sheet.json --workbook WB # blocks: heading / text / table_view / spacerd2b sheets render report --workbook WB -o report.xlsxd2b transforms list --workbook WBd2b charts list --workbook WBd2b versions commit 2026-06 --workbook WBd2b versions revert 2026-06 --workbook WBd2b jobs wait JOB_ID --timeout 1800The git round trip
Section titled “The git round trip”d2b pull / d2b push / d2b github-workflow — see Your workbook in git.
Notes for agent use
Section titled “Notes for agent use”- For agents with short Bash timeouts, “async upload →
jobs wait” is safer thanupload --wait - On a 409 (ConflictError): re-read
edit_versionwithd2b tables rows NAME --workbook $WB, re-apply, retry (never overwrite blindly) - The snippet to paste into a repository lives in Using D2B from coding agents
--wait and the API’s async=true
Section titled “--wait and the API’s async=true”| CLI | API | Behavior |
|---|---|---|
d2b upload FILE --wait | POST .../sources?async=true + job polling | accepted with 202, waits for completion (auto mode only) |
d2b upload FILE (no --wait) | same, without polling | returns a job_id — wait with d2b jobs wait JOB_ID |
--mode staged | no async (always synchronous) | bytes land immediately; --wait is unnecessary (and a usage error) |
When an error message says async, it means the API parameter — on the CLI that’s --wait (such errors also ship suggested_fix_cli in CLI vocabulary, which the CLI prints).
Install guidance (pip / pipx / uvx)
Section titled “Install guidance (pip / pipx / uvx)”- As a command:
pipx install d2b-sdkoruvx --from d2b-sdk d2b(avoids pyenv shim accidents) - As a project dependency:
uv add d2b-sdk+uv run d2b - As a library (imported from Python):
pip install d2b-sdk
Derived tables can be authored from the CLI too: d2b transforms create NAME --workbook WB --sql-file f.sql --arg src=table ({{ src }} placeholders + --arg bindings keep lineage traceable). Remove a workbook you no longer need with d2b workbooks delete ID (requires workbooks:delete, which a default login holds).