npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@metabase/cli

v0.2.2

Published

Metabase CLI

Readme

metabase-cli

Command-line client for Metabase. Logs in to an instance in your browser (OAuth, Metabase v62+) or with an API key, and stores credentials securely on your machine.

Supported Metabase versions

The minimum supported server is Metabase v0.58 (major 58). Anything older is unsupported.

Commands that need more than a baseline OSS server declare it — a higher minimum major version or a premium token feature. The server version and token features are detected and cached when you run mb auth login (or mb auth list). For those commands, a preflight check runs before the first request and refuses with an actionable message (exit code 2) when:

  • the server is older than the command's minimum version, or
  • the command needs a premium feature (e.g. remote_sync, transforms) that isn't enabled.

Plain OSS commands against a v0.58+ server (the majority) carry no elevated requirement and skip the preflight entirely. When a gated command runs but the server version can't be detected (no cached probe), it proceeds with a warning rather than refusing. To bypass the check for a single run, pass --skip-preflight; to bypass it process-wide (e.g. in CI), set MB_CLI_SKIP_PREFLIGHT=1. Both are footguns — only for servers you know are patched.

Install

npm install -g @metabase/cli
mb --help

Or build from source:

bun install
bun run build
node dist/cli.mjs --help

The binary is mb. Examples below use that name.

Quick start

mb auth login --url https://metabase.example.com
mb auth status

Authentication

Credentials are stored per-profile. The default profile is named default. Use --profile <name> to manage additional profiles.

mb auth login

Log in to a Metabase instance and save the credential to a profile. Interactive login offers two methods:

  • In your browser (recommended; requires Metabase v62 or newer) — the CLI opens Metabase, you sign in with your password or SSO and approve the CLI, and a short-lived access token plus a rotating refresh token are stored. Tokens refresh automatically; you never paste a secret.
  • With an API key — paste a key from Admin settings → Authentication → API keys.

Against a server older than v62 the CLI detects the missing OAuth support and falls back to the API key prompt automatically. Supplying an API key (flag, env, or stdin) always skips the browser flow, so CI and scripts behave exactly as before.

On success the server is probed once — the rendered output shows the user, role (Admin/User), and Metabase version, and the same values are cached in <configDir>/profiles.json so later commands skip re-probing. Failure of either the auth probe (/api/user/current) or the server probe (/api/session/properties) rejects the login; an existing profile keeps its last-known-good credential and gains a lastFailure entry.

| Flag | Description | | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | | --url <url> | Metabase URL, including any subpath if the instance is hosted under one (https://my.org.com/metabase). Falls back to MB_URL, then prompts. | | --api-key <value> | API key. Skips the browser flow. Visible in shell history — pipe on stdin instead. | | --client-id <id> | Pre-registered OAuth client id (only needed when dynamic client registration is disabled on the server). | | --profile <name>, -p | Profile to write to (default: default). | | --skip-verify | Save without contacting the server (no probe, no cache). |

Non-interactive (non-TTY) login requires an API key; resolution order: --api-key → piped stdin → MB_API_KEY (first non-empty wins). Without one, non-interactive login fails rather than prompting.

mb auth login                                            # interactive: browser or API key
echo "$MB_KEY" | mb auth login --url https://m.example.com
mb auth login --url https://m.example.com < key.txt

mb auth status

Show whether a profile is authenticated. The output includes the auth method (OAuth or API key) alongside the cached user, role, and server version.

mb auth status
mb auth status --json
mb auth status --profile staging

| Flag | Description | | ------------------------ | ---------------------------------------- | | --profile <name>, -p | Profile to inspect (default: default). | | --json | Emit JSON. Auto-enabled on non-TTY. |

mb auth list

List configured authentication profiles. All profile metadata (URL, auth method, last successful probe, last failure) lives in <configDir>/profiles.json at mode 0600; the secrets (API key, or OAuth access/refresh tokens) sit in the OS keychain when available, or inline in the same file when the keychain is unavailable.

auth list re-probes every profile, one at a time — a probe can refresh and rewrite an expired OAuth token, so probes are serialized to avoid racing on the shared profiles.json. On success it refreshes lastProbe (Metabase version, token features, user identity) and clears lastFailure; on failure it updates lastFailure and leaves the prior lastProbe/url/credential untouched. Rendered columns: Profile | URL | Auth | Status | Role | Version | Last probed. Failed rows append a one-line footer pointing at mb auth login --profile <name>.

mb auth list
mb auth list --json

| Flag | Description | | -------- | ----------------------------------- | | --json | Emit JSON. Auto-enabled on non-TTY. |

mb auth logout

Clear stored credentials for a profile. For an OAuth profile the refresh token is also revoked server-side, best-effort: local credentials are cleared first and a revocation failure only warns, so a slow or offline server never blocks the logout.

mb auth logout --yes
mb auth logout --profile staging --yes

| Flag | Description | | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------- | | --profile <name>, -p | Profile to clear (default: default). | | --yes | Skip the interactive confirmation prompt. In non-TTY contexts the prompt is skipped automatically (kubectl/gh/docker convention). |

Transforms

CRUD on /api/transform. Bodies for create / update are JSON; resolution order: --body--file → piped stdin (auto-detected when stdin is not a TTY).

mb transform list

mb transform list
mb transform list --json

mb transform get <id>

mb transform get 1 --json

mb transform dependencies <id>

List the upstream transforms this transform depends on (the ones that must run before it). The positional id is a transform id.

mb transform dependencies 1 --json

mb transform create

cat transform.json | mb transform create
mb transform create --file transform.json

| Flag | Description | | --------------- | ----------------------- | | --body <json> | Inline JSON body. | | --file <path> | Path to JSON body file. |

mb transform update <id>

mb transform update 1 --body '{"name":"renamed"}'

Same --body / --file resolution as create. Stdin is auto-detected when not a TTY.

mb transform delete <id>

mb transform delete 1 --yes

| Flag | Description | | ------- | --------------------------------------------------------------------------------------------------------------------------------- | | --yes | Skip the interactive confirmation prompt. In non-TTY contexts the prompt is skipped automatically (kubectl/gh/docker convention). |

mb transform run <id>

Trigger a manual run. Returns {message, run_id} and exits immediately. Pass --wait to poll until the run reaches a terminal status (succeeded, failed, timeout, canceled); the final field on the result holds the polled run state, and the command exits 1 if the final status is anything but succeeded. Pass --sync to additionally wait until the run's output table is registered and surface its target_table_id, so you can build MBQL cards against it — the run registers the table itself, so no separate db sync-schema is needed; --sync implies --wait.

mb transform run 1
mb transform run 1 --wait --json
mb transform run 1 --sync --json

| Flag | Description | | ----------------- | ---------------------------------------------------------------------------------------------------------------------- | | --wait | Poll until the run reaches a terminal status. | | --sync | After a successful run, wait until the output table is registered and return its target_table_id (implies --wait). | | --timeout <ms> | Polling timeout in ms (default 600000). Used with --wait. | | --interval <ms> | Polling interval in ms (default 2000). Used with --wait. |

mb transform cancel <id>

Cancel the currently-running run for a transform. Exits 0 with {canceled: true, id} on success; exits 1 with a 404 if the transform has no active run.

mb transform cancel 1
mb transform cancel 1 --json

mb transform get-run <run-id>

Fetch a single run by run id (not transform id). Same compact / --full projection convention as transform get.

mb transform get-run 1 --json

mb transform runs

List recent transform runs across all transforms, or filter to one. Drains all pages by default; pass --limit to cap.

mb transform runs
mb transform runs --transform-id 1 --json
mb transform runs --limit 10 --json

| Flag | Description | | --------------------- | --------------------------------------------------- | | --transform-id <id> | Filter to runs of a single transform id. | | --limit <n> | Cap total runs returned (default: drain all pages). |

Transform jobs

CRUD on /api/transform-job. Bodies for create / update follow the same --body / --file / stdin pattern as transforms.

mb transform-job list

mb transform-job list --json

mb transform-job get <id>

mb transform-job get 1 --json

mb transform-job create

mb transform-job create --body '{"name":"daily","schedule":"0 0 0 * * ?"}'

| Flag | Description | | --------------- | ----------------------- | | --body <json> | Inline JSON body. | | --file <path> | Path to JSON body file. |

mb transform-job update <id>

mb transform-job update 1 --body '{"schedule":"0 0 6 * * ?"}'

mb transform-job delete <id>

mb transform-job delete 1 --yes

| Flag | Description | | ------- | --------------------------------------------------------------------------------------------------------------------------------- | | --yes | Skip the interactive confirmation prompt. In non-TTY contexts the prompt is skipped automatically (kubectl/gh/docker convention). |

mb transform-job run <id>

Trigger a job manually and return immediately. The job runs every transform carrying one of the job's tags, plus those transforms' dependencies.

mb transform-job run 1
mb transform-job run 1 --force-refresh --json

| Flag | Description | | ----------------- | --------------------------------------------------------------------------------------------------- | | --force-refresh | Re-run the whole plan, including dependency transforms that are already fresh (skipped by default). |

mb transform-job transforms <id>

List the transforms a job would run, resolved by the job's tags. The positional id is a job id.

mb transform-job transforms 1 --json

mb transform-job set-active <true|false>

Activate or deactivate every transform job at once (admin only). Inactive jobs do not run on schedule; manual runs ignore the flag.

mb transform-job set-active false
mb transform-job set-active true --json

Transform tags

CRUD on /api/transform-tag. Tags group transforms and jobs; reference them by id via the tag_ids field on a transform or job. The four built-in tags (hourly, daily, weekly, monthly) drive the built-in jobs. There is no get-by-id endpoint — use list.

mb transform-tag list

mb transform-tag list --json

mb transform-tag create

mb transform-tag create --body '{"name":"nightly"}'

| Flag | Description | | --------------- | ----------------------- | | --body <json> | Inline JSON body. | | --file <path> | Path to JSON body file. |

mb transform-tag update <id>

mb transform-tag update 5 --body '{"name":"renamed"}'

mb transform-tag delete <id>

mb transform-tag delete 5 --yes

| Flag | Description | | ------- | --------------------------------------------------------------------------------------------------------------------------------- | | --yes | Skip the interactive confirmation prompt. In non-TTY contexts the prompt is skipped automatically (kubectl/gh/docker convention). |

Databases

Read warehouse metadata from /api/database. The db group exposes the full database list, the per-database record with optional table/field hydration, schema and table inspection, and the two manual-sync triggers.

db is aliased to database.

Agent traversal — the hydration ladder: start with db get <db-id> --include tables, the compact table map (id, name, schema, description per table) — one call that fits most databases. Pick the relevant tables, then fetch fields per table with table fields <table-id> (bounded: a table has at most a few hundred fields). --include tables.fields is the full rollup — small databases only. When output outgrows the --max-bytes cap, the error message names the next command down the ladder. On warehouses with hundreds of tables, traverse by schema (db schemas <db-id>db schema-tables <db-id> <schema>) or find tables by name (mb search <term> --models table --db-id <db-id>).

mb db list

mb db list
mb db list --json
mb db list --saved --json
mb db list --include tables --json   # every db with its compact table map

| Flag | Description | | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | --include <which> | Hydrate related entities. Currently only tables is supported (each database is returned with its compact tables). To map a single warehouse, prefer db get <id> --include tables. | | --saved | Include the Saved Questions virtual database in the list. The virtual db has id -1337 and no engine. |

mb db get <id>

mb db get 1
mb db get 1 --json
mb db get 1 --include tables --json          # + compact table map (fits most databases)
mb db get 1 --include tables.fields --json   # + every field of every table (small databases only)

| Flag | Description | | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | --include <which> | Hydrate related entities. tables is the compact table map — the recommended first call for schema discovery. tables.fields is the full rollup and fits only small databases; on anything larger, take the map and fetch fields per table with table fields <table-id>, or traverse by schema (see above). |

mb db schemas <id>

List the schemas in a database. Schemas with no tables are excluded. Cheap and bounded — this is the right entry point for an agent walking a warehouse.

mb db schemas 1
mb db schemas 1 --json

mb db schema-tables <id> <schema>

List the tables in one schema, sorted by display name. Returns compact projections without fields — pair with table get --include fields (or table fields <id>) per table you actually need to introspect.

mb db schema-tables 1 public
mb db schema-tables 1 analytics --json

mb db sync-schema <id>

Trigger a manual schema sync (POST /api/database/:id/sync_schema). Returns { id, status: "ok" } once the sync has been queued; the actual work happens asynchronously on the server. Pass --wait to poll the database until its initial_sync_status reports complete (a database that has already finished its initial sync returns at once). To wait for a specific newly-materialized transform table to register, prefer mb transform run <id> --sync.

mb db sync-schema 1
mb db sync-schema 1 --wait --json

| Flag | Description | | ----------------- | ----------------------------------------------------------- | | --wait | Poll until initial_sync_status reports complete. | | --timeout <ms> | Polling timeout in ms (default 600000). Used with --wait. | | --interval <ms> | Polling interval in ms (default 2000). Used with --wait. |

mb db rescan-values <id>

Trigger a rescan of cached field values (POST /api/database/:id/rescan_values). Returns { id, status: "ok" } once the rescan has been queued.

mb db rescan-values 1
mb db rescan-values 1 --json

Tables

Inspect and edit warehouse tables via /api/table. For agent-driven field introspection, table get --include fields is the default — it returns the table plus its columns in a single bounded response.

mb table list

Returns every table in the chosen database (or across all databases) as a flat compact list — no fields, no per-table hydration. On a real warehouse with hundreds of tables this is still bounded (kilobytes), but db schema-tables <db-id> <schema> is the better starting point when you know the schema.

mb table list
mb table list --db-id 1 --json

| Flag | Description | | -------------- | ----------------------------------- | | --db-id <id> | Filter tables by their database id. |

mb table get <id>

Returns the basic table record (no fields). Pass --include fields to route through /api/table/:id/query_metadata so the response carries the table's columns compact-projected as fields — this is the default agent path for field introspection (the response also carries FK targets and dimensions under --full). Use mb table fields <id> if you only want the fields as a list envelope.

mb table get 42
mb table get 42 --json
mb table get 42 --include fields --json

| Flag | Description | | ------------------- | --------------------------------------------------------------------------------------------------- | | --include <which> | Hydrate related entities. Currently only fields is supported (bundles compact-projected columns). |

mb table fields <id>

List the fields on a table (a thin projection over query_metadata.fields). Use this when you want just the field array without the surrounding table metadata.

mb table fields 42
mb table fields 42 --json

mb table update <id>

Patch a table (PUT /api/table/:id). Body fields: display_name, description, caveats, points_of_interest, entity_type, visibility_type, field_order, show_in_getting_started. Pass the body via --body, --file, or stdin (exactly one).

mb table update 42 --body '{"display_name":"Customers"}'
mb table update 42 --file patch.json
echo '{"description":"Customer dimension"}' | mb table update 42

Publish status surfaces on the table itself — table get/table list carry is_published (and collection_id under --full). Publishing tables to the Library is done with mb library publish.

Fields

Inspect and edit individual columns via /api/field.

mb field get <id>

mb field get 100
mb field get 100 --json

mb field values <id>

Fetch the cached distinct values list (GET /api/field/:id/values). Returns the FieldValues envelope ({ values, field_id, has_more_values }); empty values on fields whose has_field_values is none or search.

mb field values 100 --json

mb field summary <id>

Row count and distinct count for the field (GET /api/field/:id/summary). Metabase returns this as an array-of-pairs; the CLI normalizes it to { field_id, count, distincts }.

mb field summary 100
mb field summary 100 --json

mb field update <id>

Patch a field (PUT /api/field/:id). Body fields: display_name, description, caveats, points_of_interest, semantic_type, coercion_strategy, fk_target_field_id, visibility_type, has_field_values, settings, nfc_path, json_unfolding. Pass the body via --body, --file, or stdin.

mb field update 100 --body '{"description":"customer email","semantic_type":"type/Email"}'
mb field update 100 --file patch.json

Upload

Load CSV/TSV data into the warehouse via /api/upload. Requires an uploads database configured on the server (Admin → Settings → Uploads); the destination db and schema are set there, not per-command. append/replace target a table created by a prior upload, and the CSV columns must match.

mb upload csv

Create a new table plus a model over it from a CSV file. Prints the new model id and table id.

mb upload csv --file data.csv
mb upload csv --file data.csv --collection 5
mb upload csv --file data.csv --json

| Flag | Description | | ------------------- | ---------------------------------------------------------------------- | | --file <path> | Path to the CSV/TSV file to upload (required). | | --collection <id> | Target collection id for the created model, or root (default: root). |

mb upload append <table-id>

Insert a CSV file's rows into an existing uploaded table.

mb upload append 42 --file more-rows.csv
mb upload append 42 --file more-rows.csv --json

mb upload replace <table-id>

Replace an existing uploaded table's contents with a CSV file's rows.

mb upload replace 42 --file rows.csv
mb upload replace 42 --file rows.csv --json

Cards

CRUD plus query execution on /api/card. A "card" is a Metabase question, model, or metric. The query subcommand runs the card and either returns Metabase's JSON envelope or streams a raw CSV / XLSX export.

mb card list

mb card list
mb card list --filter archived --json
mb card list --filter using_model --model-id 42 --json

| Flag | Description | | ------------------- | -------------------------------------------------------------------------------------------------------------- | | --filter <preset> | One of all (default), mine, bookmarked, database, table, archived, using_model, using_segment. | | --model-id <id> | Required when --filter is database, table, using_model, or using_segment. |

mb card get <id>

mb card get 1
mb card get 1 --json --full

mb card query <id>

Run the card's query. Without --export-format, returns the Metabase JSON envelope (status, row_count, data: { rows, cols }, …). With --export-format csv, --export-format json, or --export-format xlsx, the export bytes stream straight to stdout.

mb card query 1 --json
mb card query 1 --json --limit 20
mb card query 1 --export-format csv > export.csv
mb card query 1 --export-format json > export.json
mb card query 1 --export-format xlsx > export.xlsx
mb card query 1 --parameters '[{"type":"category","value":"A","target":["variable",["template-tag","c"]]}]'

| Flag | Description | | ----------------------- | ------------------------------------------------------------------------------------------------------ | | --export-format <fmt> | Stream the export instead of the JSON envelope. One of csv, json, xlsx. | | --parameters <json> | JSON array of Metabase parameter objects (the same shape Metabase POSTs from a dashboard). | | --limit <n> | Cap rows kept in the JSON envelope. No effect on streamed exports. | | --format-rows | Streamed exports only: apply the card's visualization-settings formatting to values (default false). | | --pivot-results | Streamed exports only: emit the pivoted output for pivot questions (default false). |

mb card alerts <id>

List the alerts watching this card. Manage them with mb alert create|update|send|archive, which take the alert id printed here.

mb card alerts 94
mb card alerts 94 --include-inactive --json

| Flag | Description | | -------------------- | ----------------------------------- | | --include-inactive | Include archived (inactive) alerts. |

mb card create

cat card.json | mb card create
mb card create --file card.json
mb card create --body '{"name":"x","display":"table","dataset_query":{...},"visualization_settings":{}}'

| Flag | Description | | --------------- | ----------------------- | | --body <json> | Inline JSON body. | | --file <path> | Path to JSON body file. |

mb card update <id>

Patch a card. Body is a partial subset of the create shape (name, display, dataset_query, visualization_settings, description, archived, collection_id, dashboard_id, cache_ttl, parameters, parameter_mappings, etc.). Only the keys you send are touched. If dataset_query is MBQL 5 (lib/type: "mbql/query") it goes through the same pre-flight validation as card create and mb query; pass --skip-validate to bypass.

cat patch.json | mb card update 1
mb card update 1 --file patch.json
mb card update 1 --body '{"name":"renamed"}'
mb card update 1 --body '{"display":"bar"}'
mb card update 1 --body '{"archived":true}'
mb card update 1 --file patch.json --skip-validate

| Flag | Description | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | --body <json> | Inline JSON body. | | --file <path> | Path to JSON body file. | | --skip-validate | Skip the local MBQL 5 pre-flight validation; let the server be the authority. Use only when the bundled schema disagrees with what the server accepts. |

mb card archive <id>

Soft-delete a card by setting archived: true. The archived card stays available via card list --filter archived and card get <id> until permanently deleted server-side. To unarchive (or otherwise toggle the flag) use mb card update <id> --body '{"archived":false}'.

mb card archive 1
mb card archive 1 --json

Dashboards

Read and write dashboards on /api/dashboard. A dashboard groups cards (questions, models, metrics) into a single layout. Each card on a dashboard is a "dashcard" — a placement record with its own id, position (row/col), and size (size_x/size_y). Dashcards live nested inside the parent dashboard's dashcards array; the API has no per-dashcard endpoint, so single-dashcard edits round-trip through PUT /api/dashboard/:id.

mb dashboard list

mb dashboard list
mb dashboard list --json
mb dashboard list --filter archived --json

| Flag | Description | | ------------------- | ------------------------------------------- | | --filter <preset> | One of all (default), mine, archived. |

mb dashboard get <id>

mb dashboard get 1
mb dashboard get 1 --json
mb dashboard get 1 --json --full

--full returns the full hydrated dashboard including the dashcards and tabs arrays. The default compact view returns only id, name, description, archived, and collection_id.

mb dashboard cards <id>

List the dashcards on a dashboard.

mb dashboard cards 1
mb dashboard cards 1 --json

mb dashboard subscriptions <id>

List the subscriptions delivering this dashboard. Manage them with mb subscription create|update|archive, which take the subscription id printed here.

mb dashboard subscriptions 10
mb dashboard subscriptions 10 --json

| Flag | Description | | ------------ | --------------------------------------------------- | | --archived | Show archived subscriptions instead of active ones. |

Dashboard parameters (filters)

A dashboard's parameters are its filter widgets. They're typed (Parameter schema): an invalid type is rejected at the CLI boundary with a message that echoes the full allowed enum (string/=, string/contains, number/between, date/range, category, id, temporal-unit, …).

Read them off the dashboard with mb dashboard get <id> --fields parameters --json (or --full for the whole record). There is no separate read verb — they're part of the dashboard.

Editing replaces the whole parameters array, so it's a read-modify-write loop: read the current set, modify it, and send it all back via mb dashboard create/mb dashboard update --body '{"parameters":[…]}'; omitting a parameter deletes it. Each parameter's id is a descriptive string you choose (reuse the slug, e.g. order_status), unique within the dashboard — Metabase stores any non-blank string as-is, so there is no need to generate a random id (use mb uuid only if you genuinely want an opaque one). Bind a parameter to a card column through a dashcard's parameter_mappings, whose parameter_id must match a parameter id exactly.

mb dashboard parameter-values <dashboard-id> <parameter-id>

Fetch the selectable values for one dashboard parameter ({values, has_more_values}). Values come from the parameter's static list, its source card, or — for a parameter mapped to a field — the field's live distinct values (chain-filtered).

mb dashboard parameter-values 1 order_status --json
mb dashboard parameter-values 1 order_status --query Cam --json

| Flag | Description | | ------------------ | -------------------------------------------------------------------------------------- | | --query <substr> | Case-insensitive substring search (first 1000 matches) instead of the full value list. |

mb dashboard create

The body accepts the same dashboard-level fields as the underlying POST /api/dashboard (name, description, parameters, cache_ttl, collection_id, collection_position). It also accepts optional dashcards and tabs: when either is present, the CLI chains a PUT /api/dashboard/:id after the create and returns the updated dashboard with its dashcards/tabs applied. Use a negative id on a dashcard to indicate one the server should newly create.

cat dashboard.json | mb dashboard create
mb dashboard create --file dashboard.json
mb dashboard create --body '{"name":"My Dashboard","collection_id":4}'
mb dashboard create --body '{"name":"D","dashcards":[{"id":-1,"card_id":42,"row":0,"col":0,"size_x":12,"size_y":6}]}'

| Flag | Description | | --------------- | --------------------------------------------------- | | --body <json> | Inline JSON body. | | --file <path> | Path to JSON body file. Use - to read from stdin. |

mb dashboard update <id>

Patch a dashboard. To edit the dashcard set, send the entire dashcards array — IDs not in the array get deleted, and a negative id indicates a new dashcard the server should create.

cat patch.json | mb dashboard update 1
mb dashboard update 1 --file patch.json
mb dashboard update 1 --body '{"name":"renamed"}'
mb dashboard update 1 --body '{"dashcards":[{"id":-1,"card_id":42,"row":0,"col":0,"size_x":12,"size_y":6}]}'

mb dashboard update-dashcard <dashboard-id> <dashcard-id>

Patch a single dashcard's layout or settings. The command does the round-trip for you: GET /api/dashboard/:id, merges the patch into the targeted dashcard while preserving every other dashcard verbatim, then PUTs the whole array back.

mb dashboard update-dashcard 1 5 --body '{"row":2,"col":0}'
mb dashboard update-dashcard 1 5 --body '{"size_x":12,"size_y":4}'
cat patch.json | mb dashboard update-dashcard 1 5

| Patch field | Type | | ------------------------ | ---------------------------------- | | row, col | non-negative integer | | size_x, size_y | positive integer | | dashboard_tab_id | integer or null | | parameter_mappings | array of parameter-mapping objects | | inline_parameters | array of strings | | visualization_settings | object |

The patch must contain at least one field; an empty object is rejected before the network round-trip.

mb dashboard archive <id>

Soft-delete a dashboard by setting archived: true. The archived dashboard stays available via dashboard list --filter archived and dashboard get <id> until permanently deleted server-side. To unarchive use mb dashboard update <id> --body '{"archived":false}'.

mb dashboard archive 1
mb dashboard archive 1 --json

Snippets

CRUD on /api/native-query-snippet. A snippet is a named, reusable piece of native (SQL) query text — referenced from cards via {{snippet: Name}}. The list endpoint returns either active or archived rows (mutually exclusive — pass --archived to swap).

mb snippet list

mb snippet list
mb snippet list --json
mb snippet list --archived --json

| Flag | Description | | ------------ | ---------------------------------------------- | | --archived | Show archived snippets instead of active ones. |

mb snippet get <id>

mb snippet get 1
mb snippet get 1 --json --full

mb snippet create

cat snippet.json | mb snippet create
mb snippet create --file snippet.json
mb snippet create --body '{"name":"active","content":"WHERE active = true"}'

| Flag | Description | | --------------- | ----------------------- | | --body <json> | Inline JSON body. | | --file <path> | Path to JSON body file. |

Body fields: name (required), content (required), description (optional), collection_id (optional positive integer).

mb snippet update <id>

Patch a snippet. Body is a partial subset of the create shape plus archived. Only the keys you send are touched.

cat patch.json | mb snippet update 1
mb snippet update 1 --file patch.json
mb snippet update 1 --body '{"name":"renamed"}'
mb snippet update 1 --body '{"archived":true}'

| Flag | Description | | --------------- | ----------------------- | | --body <json> | Inline JSON body. | | --file <path> | Path to JSON body file. |

mb snippet archive <id>

Soft-delete a snippet by setting archived: true. To unarchive use mb snippet update <id> --body '{"archived":false}'.

mb snippet archive 1
mb snippet archive 1 --json

Segments

CRUD on /api/segment. A segment is a saved MBQL filter macro tied to a table — used in card filters to share a reusable predicate. Mutating endpoints require a revision_message for the audit log.

mb segment list

mb segment list
mb segment list --json

mb segment get <id>

mb segment get 1
mb segment get 1 --json --full

mb segment create

cat segment.json | mb segment create
mb segment create --file segment.json
mb segment create --file segment.json --skip-validate

| Flag | Description | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | --body <json> | Inline JSON body. | | --file <path> | Path to JSON body file. | | --skip-validate | Skip the local MBQL 5 pre-flight validation; let the server be the authority. Use only when the bundled schema disagrees with what the server accepts. |

Body fields: name (required), table_id (required positive integer), definition (required MBQL filter object), description (optional). If definition is MBQL 5 (lib/type: "mbql/query") it goes through the same pre-flight validation as card create and mb query; pass --skip-validate to bypass.

mb segment update <id>

Patch a segment. The body MUST include revision_message. Other keys are partial: name, definition, archived, description, caveats, points_of_interest, show_in_getting_started. If definition is MBQL 5 (lib/type: "mbql/query") it goes through the same pre-flight validation as segment create; pass --skip-validate to bypass.

cat patch.json | mb segment update 1
mb segment update 1 --file patch.json
mb segment update 1 --body '{"name":"renamed","revision_message":"rename"}'
mb segment update 1 --file patch.json --skip-validate

| Flag | Description | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | --body <json> | Inline JSON body. | | --file <path> | Path to JSON body file. | | --skip-validate | Skip the local MBQL 5 pre-flight validation; let the server be the authority. Use only when the bundled schema disagrees with what the server accepts. |

mb segment archive <id>

Soft-delete a segment by setting archived: true. The default revision message is "Archived via mb CLI"; override with --revision-message.

mb segment archive 1
mb segment archive 1 --revision-message "deprecated"

| Flag | Description | | --------------------------- | ------------------------------------------- | | --revision-message <text> | Audit-log message recorded with the change. |

Measures

CRUD on /api/measure. A measure is a saved MBQL aggregation (a single :aggregation clause) tied to a table — referenced from cards and metrics to share a reusable computation. Mutating endpoints require a revision_message for the audit log.

mb measure list

mb measure list
mb measure list --json

mb measure get <id>

mb measure get 1
mb measure get 1 --json --full

mb measure create

cat measure.json | mb measure create
mb measure create --file measure.json
mb measure create --file measure.json --skip-validate

| Flag | Description | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | --body <json> | Inline JSON body. | | --file <path> | Path to JSON body file. | | --skip-validate | Skip the local MBQL 5 pre-flight validation; let the server be the authority. Use only when the bundled schema disagrees with what the server accepts. |

Body fields: name (required), table_id (required positive integer), definition (required MBQL aggregation object), description (optional). If definition is MBQL 5 (lib/type: "mbql/query") it goes through the same pre-flight validation as card create and mb query; pass --skip-validate to bypass.

mb measure update <id>

Patch a measure. The body MUST include revision_message. Other keys are partial: name, definition, archived, description. If definition is MBQL 5 (lib/type: "mbql/query") it goes through the same pre-flight validation as measure create; pass --skip-validate to bypass.

cat patch.json | mb measure update 1
mb measure update 1 --file patch.json
mb measure update 1 --body '{"name":"renamed","revision_message":"rename"}'
mb measure update 1 --file patch.json --skip-validate

| Flag | Description | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | --body <json> | Inline JSON body. | | --file <path> | Path to JSON body file. | | --skip-validate | Skip the local MBQL 5 pre-flight validation; let the server be the authority. Use only when the bundled schema disagrees with what the server accepts. |

mb measure archive <id>

Soft-delete a measure by setting archived: true. The default revision message is "Archived via mb CLI"; override with --revision-message.

mb measure archive 1
mb measure archive 1 --revision-message "deprecated"

| Flag | Description | | --------------------------- | ------------------------------------------- | | --revision-message <text> | Audit-log message recorded with the change. |

Timelines

CRUD on /api/timeline. A timeline is a named collection of dated events rendered as annotations on time-series charts. Timelines live in collections (collection_id: null = root) and carry an icon (star, cake, mail, warning, bell, cloud).

mb timeline list

mb timeline list
mb timeline list --json
mb timeline list --archived --json

| Flag | Description | | ------------ | ----------------------------------------------- | | --archived | Show archived timelines instead of active ones. |

mb timeline get <id>

mb timeline get 1
mb timeline get 1 --json --full

mb timeline events <id>

List the events on a timeline. Archived events are excluded unless --archived is passed (which returns both).

mb timeline events 1
mb timeline events 1 --archived --json

| Flag | Description | | ------------ | ------------------------ | | --archived | Include archived events. |

mb timeline create

mb timeline create --body '{"name":"Releases"}'
cat timeline.json | mb timeline create
mb timeline create --file timeline.json

| Flag | Description | | --------------- | ----------------------- | | --body <json> | Inline JSON body. | | --file <path> | Path to JSON body file. |

Body fields: name (required), description (optional), icon (optional, default star), collection_id (optional positive integer, omit for the root collection), default (optional boolean marking the collection's default timeline).

mb timeline update <id>

Patch a timeline. Body is a partial subset of the create shape plus archived. Only the keys you send are touched. Changing archived cascades to every event on the timeline.

mb timeline update 1 --body '{"name":"Product releases"}'
cat patch.json | mb timeline update 1
mb timeline update 1 --file patch.json

| Flag | Description | | --------------- | ----------------------- | | --body <json> | Inline JSON body. | | --file <path> | Path to JSON body file. |

mb timeline archive <id>

Soft-delete a timeline (and, by server-side cascade, all its events) by setting archived: true. To unarchive use mb timeline update <id> --body '{"archived":false}'.

mb timeline archive 1
mb timeline archive 1 --json

mb timeline delete <id>

Permanently delete a timeline and all its events. Irreversible — prefer mb timeline archive unless you mean it. Prompts for confirmation on a TTY; requires --yes otherwise.

mb timeline delete 1 --yes
mb timeline delete 1

| Flag | Description | | ------- | ------------------ | | --yes | Skip confirmation. |

Timeline events

CRUD on /api/timeline-event. An event is a dated annotation on a timeline. There is no server-side list endpoint — list events with mb timeline events <id>.

mb timeline-event get <id>

mb timeline-event get 1
mb timeline-event get 1 --json --full

mb timeline-event create

mb timeline-event create --body '{"name":"v2 launch","timestamp":"2026-07-01T00:00:00Z","timezone":"UTC","time_matters":false,"timeline_id":1}'
cat event.json | mb timeline-event create
mb timeline-event create --file event.json

| Flag | Description | | --------------- | ----------------------- | | --body <json> | Inline JSON body. | | --file <path> | Path to JSON body file. |

Body fields: name (required), timestamp (required, ISO 8601), timezone (required, IANA name like UTC or America/New_York), time_matters (required boolean — true when the time of day is significant, false when only the date is), timeline_id (required positive integer), description (optional), icon (optional, default: the timeline's icon).

mb timeline-event update <id>

Patch an event. Body is a partial subset of the create shape plus archived. Only the keys you send are touched; timeline_id moves the event to another timeline.

mb timeline-event update 1 --body '{"name":"v2.1 launch"}'
cat patch.json | mb timeline-event update 1
mb timeline-event update 1 --file patch.json

| Flag | Description | | --------------- | ----------------------- | | --body <json> | Inline JSON body. | | --file <path> | Path to JSON body file. |

mb timeline-event archive <id>

Soft-delete an event by setting archived: true. To unarchive use mb timeline-event update <id> --body '{"archived":false}'.

mb timeline-event archive 1
mb timeline-event archive 1 --json

mb timeline-event delete <id>

Permanently delete an event. Prompts for confirmation on a TTY; requires --yes otherwise.

mb timeline-event delete 1 --yes
mb timeline-event delete 1

| Flag | Description | | ------- | ------------------ | | --yes | Skip confirmation. |

Dashboard subscriptions

Read and write dashboard subscriptions on /api/pulse. A subscription delivers a rendered dashboard on a schedule — by email, to a Slack channel, or to an HTTP webhook. It pins the dashboard's cards by both id (the card) and dashboard_card_id (its placement); mb dashboard cards <dashboard-id> prints both.

A subscription's dashboard_id and collection_id are fixed at creation. There is no delete — archiving is the terminal state, and it also disables every channel.

mb subscription list

mb subscription list
mb subscription list --dashboard-id 10 --json
mb subscription list --archived --json

| Flag | Description | | --------------------- | --------------------------------------------------- | | --dashboard-id <id> | Only subscriptions on this dashboard. | | --archived | Show archived subscriptions instead of active ones. |

Listing from the dashboard side is mb dashboard subscriptions <dashboard-id>.

mb subscription get <id>

mb subscription get 1
mb subscription get 1 --full --json

The compact view returns id, name, dashboard_id, collection_id, archived, skip_if_empty, plus the pinned cards and the channels with their schedules and recipients. --full adds the hydrated creator, entity ids, and per-card download permissions.

mb subscription create

The body needs name, dashboard_id, cards, and channels.

Each channel names a channel_type (email, slack, http) and a schedule_type (hourly, daily, weekly, monthly) plus the fields that schedule needs: daily needs schedule_hour (0–23); weekly also needs schedule_day (monsun); monthly also needs schedule_frame (first, mid, last). Email recipients are {"email":"[email protected]"} or {"id":<user-id>}; Slack targets a channel with "details":{"channel":"#general"}. A channel is enabled unless you say otherwise.

mb subscription create --body '{"name":"Weekly orders","dashboard_id":10,"cards":[{"id":94,"dashboard_card_id":87,"include_csv":false,"include_xls":false}],"channels":[{"channel_type":"email","schedule_type":"daily","schedule_hour":8,"recipients":[{"email":"[email protected]"}]}]}'
cat subscription.json | mb subscription create
mb subscription create --file subscription.json

| Flag | Description | | --------------- | --------------------------------------------------- | | --body <json> | Inline JSON body. | | --file <path> | Path to JSON body file. Use - to read from stdin. |

mb subscription update <id>

Patches name, cards, channels, skip_if_empty, parameters, archived. cards and channels each replace the whole list, so send every one you want to keep — mb subscription get <id> --full prints the current set.

The update reads the subscription first and carries archived and skip_if_empty forward when your patch omits them. That is load-bearing: PUT /api/pulse/:id defaults every omitted key, and both of those default to false, so a raw name-only PUT would un-archive the subscription and clear skip_if_empty.

mb subscription update 1 --body '{"name":"Daily orders"}'
mb subscription update 1 --body '{"channels":[{"channel_type":"email","schedule_type":"weekly","schedule_hour":8,"schedule_day":"mon","recipients":[{"email":"[email protected]"}]}]}'
mb subscription update 1 --file patch.json

| Flag | Description | | --------------- | --------------------------------------------------- | | --body <json> | Inline JSON body. | | --file <path> | Path to JSON body file. Use - to read from stdin. |

mb subscription archive <id>

Archive a subscription, stopping all deliveries. Also disables every channel on it, so restoring means un-archiving and then re-enabling the channels.

mb subscription archive 1
mb subscription archive 1 --json

Question alerts

Read and write question alerts on /api/notification. An alert watches one card and delivers it when a send condition fires on a schedule: has_result (the card returned any row), or goal_above / goal_below (both need a goal set on the card's visualization).

Schedules are Quartz cron strings — 0 0 8 * * ? * is daily at 08:00. /api/notification also carries Metabase's internal system-event notifications; mb alert scopes every request to card alerts, so they never appear.

Archiving deactivates an alert rather than deleting it: mb alert list --include-inactive still finds it, and mb alert update <id> --body '{"active":true}' brings it back.

mb alert list

mb alert list
mb alert list --card-id 94 --json
mb alert list --include-inactive --json

| Flag | Description | | --------------------- | ----------------------------------- | | --card-id <id> | Only alerts watching this card. | | --creator-id <id> | Only alerts created by this user. | | --recipient-id <id> | Only alerts delivered to this user. | | --include-inactive | Include archived (inactive) alerts. |

Listing from the question side is mb card alerts <card-id>.

mb alert get <id>

mb alert get 9
mb alert get 9 --full --json

The compact view returns id, active, creator_id, the payload (card_id, send_condition, send_once), the cron subscriptions, and the handlers with their recipients. --full adds the hydrated card the alert watches.

mb alert create

The body needs payload, subscriptions, and handlers. Each handler names a channel_type (channel/email, channel/slack, channel/http) and its recipients; a recipient is {"type":"notification-recipient/user","user_id":3} or {"type":"notification-recipient/raw-value","details":{"value":"[email protected]"}}.

mb alert create --body '{"payload":{"card_id":94,"send_condition":"has_result"},"subscriptions":[{"cron_schedule":"0 0 8 * * ? *"}],"handlers":[{"channel_type":"channel/email","recipients":[{"type":"notification-recipient/raw-value","details":{"value":"[email protected]"}}]}]}'
cat alert.json | mb alert create
mb alert create --file alert.json

| Flag | Description | | --------------- | --------------------------------------------------- | | --body <json> | Inline JSON body. | | --file <path> | Path to JSON body file. Use - to read from stdin. |

mb alert update <id>

Patches the top-level fields you send: payload, subscriptions, handlers, active. Fields inside payload merge over the current ones, so {"payload":{"send_condition":"goal_above"}} keeps the card. subscriptions and handlers each replace the whole list — mb alert get <id> prints the current set. An alert cannot be moved to a different card.

The update reads the alert first and merges your patch over it. That is load-bearing: PUT /api/notification/:id is a spec-diff, and a body whose id doesn't match the stored one makes Metabase delete the alert and insert a replacement under a fresh id.

mb alert update 9 --body '{"payload":{"send_condition":"goal_above"}}'
mb alert update 9 --body '{"subscriptions":[{"cron_schedule":"0 0 9 * * ? *"}]}'
mb alert update 9 --body '{"active":true}'

| Flag | Description | | --------------- | --------------------------------------------------- | | --body <json> | Inline JSON body. | | --file <path> | Path to JSON body file. Use - to read from stdin. |

mb alert send <id>

Send an alert now, off-schedule. Delivers to every handler, ignoring the send condition. The channel must be configured on the server (email needs SMTP set up).

mb alert send 9
mb alert send 9 --json

mb alert archive <id>

Archive an alert, stopping all deliveries and dropping its scheduled trigger.

mb alert archive 9
mb alert archive 9 --json

Collections

Read collections on /api/collection. Collections are the folders that contain cards, dashboards, and other collections. The list endpoint surfaces a virtual root collection (id "root") alongside regular numeric ids; the get endpoint accepts only the numeric id.

mb collection list

mb collection list
mb collection list --json
mb collection list --filter archived --json

| Flag | Description | | ------------------- | --------------------------------------------------------------------------------------------------------------- | | --filter <preset> | One of all (default), archived (returns the trash collection only), personal (only personal collections). |

mb collection get <id>

<id> accepts any of: a positive integer collection id, the literal root (the virtual "Our analytics" root), the literal trash (the trash collection), or a 21-character entity id (NanoID). Anything else is rejected with a ConfigError before any HTTP call.

mb collection get 4
mb collection get root --json
mb collection get trash --json
mb collection get voo1If9y8Sld0lXej6xl0 --json
mb collection get 4 --json --full

--full returns the full hydrated collection including slug, entity_id, can_write, namespace, and personal_owner_id. The default compact view returns id, name, description, archived, location, parent_id, type, authority_level, and is_personal. The root collection has a stripped-down shape — archived, description, location, type, etc. are absent rather than null.

mb collection items <id>

List the cards, dashboards, sub-collections, and other content stored inside a collection. The CLI drains all pages of /api/collection/:id/items; pass --limit to cap the result. <id> accepts the same forms as collection get — including root for top-level content (items there have collection_id: null).

mb collection items 4
mb collection items root --json
mb collection items 4 --models card,dashboard --json
mb collection items 4 --pinned-state is_pinned --json

| Flag | Description | | ------------------------ | ------------------------------------------------------------------------------------------------------------------ | | --models <csv> | Restrict to one or more models (card, dataset, metric, dashboard, snippet, collection, document, …). | | --archived | Return archived items instead of unarchived. | | --pinned-state <state> | One of all, is_pinned, is_not_pinned. | | --limit <n> | Cap total items returned. Default: drain all pages. |

mb collection tree

Fetch the full collection hierarchy as a nested tree. Output is always JSON — the recursive structure does not render meaningfully as a key/value table.

mb collection tree
mb collection tree --json

mb collection create

Create a collection from a JSON spec. The body accepts the same fields as POST /api/collection: name (required), description, parent_id (omit or null for the root), namespace, and authority_level.

cat collection.json | mb collection create
mb collection create --file collection.json
mb collection create --body '{"name":"My Collection","parent_id":4}'
mb collection create --body '{"name":"ETL"}' --namespace transforms

| Flag | Description | | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | --body <json> | Inline JSON body. | | --file <path> | Path to JSON body file. Use - to read from stdin. | | --namespace <ns> | Collection namespace (transforms, snippets, analytics, shared-tenant-collection, tenant-specific). Omit for a normal collection; required for a collection a transform's collection_id can point at. |

mb collection archive <id>

Soft-delete a collection by setting archived: true. The archived collection stays available via collection list --filter archived until permanently deleted server-side. Restore it from the trash in the Metabase UI.

mb collection archive 4
mb collection archive 4 --json

Library

Curate the Metabase Library — a governed subtree (library-data "Data" for published tables, library-metrics "Metrics" for official metrics, under a library root). Tables published to Data appear first when people pick a data source and rank up in search, steering everyone toward trusted, analysis-ready tables. Requires the library premium feature (Pro/Enterprise) and admin or data-analyst permission (Curate alone won't publish tables). Publish status surfaces on the table via is_published (table get/table list).

mb library get

Show the Library and its Data/Metrics collection ids (GET /api/ee/library/). Errors if the Library hasn't been created yet.

mb library get
mb library get --json

mb library create

Create the Library subtree (POST /api/ee/library/). Idempotent — returns the existing Library when it's already there.

mb library create
mb library create --json