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.1.1

Published

Metabase CLI

Readme

metabase-cli

Command-line client for Metabase. Authenticates against an instance with an API key and stores it securely on your machine.

Install

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

Or build from source:

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

The binary is metabase. Examples below use that name.

Quick start

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

Authentication

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

metabase auth login

Save credentials for a profile.

| Flag | Description | | ------------------- | ---------------------------------------------------------- | | --url <url> | Metabase URL. Falls back to METABASE_URL, then prompts. | | --api-key <value> | API key. Visible in shell history — pipe on stdin instead. | | --profile <name> | Profile to write to (default: default). | | --skip-verify | Save without contacting the server. |

Resolution order for the API key: --api-key → piped stdin → METABASE_API_KEY → interactive prompt. Stdin is auto-detected when not a TTY.

echo "$MB_KEY" | metabase auth login --url https://m.example.com
metabase auth login --url https://m.example.com < key.txt

metabase auth status

Show whether a profile is authenticated.

metabase auth status
metabase auth status --json
metabase auth status --profile staging

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

metabase auth list

List configured authentication profiles. The index is maintained at <configDir>/profiles.json and updated on every auth login / auth logout. Profiles whose URL/API key were stored in the OS keychain before the index existed are picked up by a one-time backfill from credentials.json; profiles that exist only in the keyring (no entry in credentials.json) appear after the next auth login or auth logout against them.

metabase auth list
metabase auth list --json

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

metabase auth logout

Clear stored credentials for a profile.

metabase auth logout --yes
metabase auth logout --profile staging --yes

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

License

Manage the Metabase Enterprise license token.

metabase license set [token]

Store a license token. Resolution order: positional → piped stdin → METABASE_LICENSE_TOKEN → interactive prompt. Stdin is auto-detected when not a TTY.

Common output flags (--json, --format, --detail, --fields, --max-bytes) are accepted; the result payload is rendered through the standard output layer.

echo "$MB_LICENSE" | metabase license set
metabase license set < token.txt

metabase license status

Show whether a license is stored. Does not reveal the value.

metabase license status
metabase license status --json

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

metabase license remove

Clear the stored license.

metabase license remove --yes

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

Common output flags (--json, --format, --detail, --fields, --max-bytes) are accepted; the result payload is rendered through the standard output layer.

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).

metabase transform list

metabase transform list
metabase transform list --json

metabase transform get <id>

metabase transform get 1 --json

metabase transform create

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

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

metabase transform update <id>

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

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

metabase transform delete <id>

metabase 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). |

metabase 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.

metabase transform run 1
metabase transform run 1 --wait --json

| Flag | Description | | ----------------- | ----------------------------------------------------------- | | --wait | Poll until the run reaches a terminal status. | | --timeout <ms> | Polling timeout in ms (default 600000). Used with --wait. | | --interval <ms> | Polling interval in ms (default 2000). Used with --wait. |

metabase 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.

metabase transform cancel 1
metabase transform cancel 1 --json

metabase transform get-run <run-id>

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

metabase transform get-run 1 --json

metabase transform runs

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

metabase transform runs
metabase transform runs --transform-id 1 --json
metabase 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.

metabase transform-job list

metabase transform-job list --json

metabase transform-job get <id>

metabase transform-job get 1 --json

metabase transform-job create

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

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

metabase transform-job update <id>

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

metabase transform-job delete <id>

metabase 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). |

Databases

Read warehouse metadata from /api/database. The db group exposes the full database list, the per-database record, schema and table inspection, the two manual-sync triggers, and (rarely useful) full-warehouse rollup endpoints.

db is aliased to database.

Agent traversal: prefer the granular path — db listdb schemas <db-id>db schema-tables <db-id> <schema>table get <table-id> --include fields. On a real warehouse (dozens of schemas, hundreds of tables, dozens of fields per table) the rollup commands (db metadata, db get --include tables.fields, db list --include tables) return megabytes of JSON and exhaust the agent context. Reach for them only on small/dev warehouses where you know the size up front.

metabase db list

metabase db list
metabase db list --json
metabase db list --saved --json
metabase db list --include tables --full --json   # rollup: every db with its full table list

| Flag | Description | | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | --include <which> | Hydrate related entities. Currently only tables is supported (each database is returned with its tables). On real warehouses this returns hundreds of table records per db — use the granular traversal instead. | | --saved | Include the Saved Questions virtual database in the list. The virtual db has id -1337 and no engine. |

metabase db get <id>

metabase db get 1
metabase db get 1 --json
metabase db get 1 --include tables --full --json          # rollup: db + every table (compact)
metabase db get 1 --include tables.fields --full --json   # rollup: db + every table + every field

| Flag | Description | | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | --include <which> | Hydrate related entities. One of tables or tables.fields. tables.fields returns every column of every table in the database in one response — only safe on small/dev warehouses. For a real warehouse use db schemasdb schema-tablestable get --include fields. |

metabase db metadata <id>

Equivalent to GET /api/database/:id/metadata: a single database with all its tables and fields rolled up in one response. This is the largest read in the db group — on a real warehouse the response will exceed the agent context. Use only when you know the database is small (a seeded dev instance, a sample db, a freshly-bootstrapped test fixture). For agent-driven introspection on a real warehouse, walk db schemasdb schema-tablestable get --include fields instead.

metabase db metadata 1 --json --full --max-bytes 0

metabase 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.

metabase db schemas 1
metabase db schemas 1 --json

metabase 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.

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

metabase 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.

metabase db sync-schema 1
metabase db sync-schema 1 --json

metabase 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.

metabase db rescan-values 1
metabase 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.

metabase 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.

metabase table list
metabase table list --db-id 1 --json

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

metabase 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. Use metabase table fields <id> if you only want the fields as a list envelope, or metabase table metadata <id> when you also need FKs and dimensions hydrated.

metabase table get 42
metabase table get 42 --json
metabase table get 42 --include fields --json

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

metabase 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.

metabase table fields 42
metabase table fields 42 --json

metabase table metadata <id>

GET /api/table/:id/query_metadata: the table with its fields, FKs, dimensions, segments, and measures all hydrated. Heavier than table get --include fields — reach for it only when you actually need the FK / dimension / segment / measure data.

metabase table metadata 42 --json --full --max-bytes 0

metabase 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).

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

Fields

Inspect and edit individual columns via /api/field.

metabase field get <id>

metabase field get 100
metabase field get 100 --json

metabase 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.

metabase field values 100 --json

metabase 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 }.

metabase field summary 100
metabase field summary 100 --json

metabase 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.

metabase field update 100 --body '{"description":"customer email","semantic_type":"type/Email"}'
metabase field update 100 --file patch.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.

metabase card list

metabase card list
metabase card list --filter archived --json
metabase 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. |

metabase card get <id>

metabase card get 1
metabase card get 1 --json --detail full

metabase 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.

metabase card query 1 --json
metabase card query 1 --json --limit 20
metabase card query 1 --export-format csv > export.csv
metabase card query 1 --export-format json > export.json
metabase card query 1 --export-format xlsx > export.xlsx
metabase 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). |

metabase card create

cat card.json | metabase card create
metabase card create --file card.json
metabase 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. |

metabase 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 metabase query; pass --skip-validate to bypass.

cat patch.json | metabase card update 1
metabase card update 1 --file patch.json
metabase card update 1 --body '{"name":"renamed"}'
metabase card update 1 --body '{"display":"bar"}'
metabase card update 1 --body '{"archived":true}'
metabase 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. |

metabase 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 metabase card update <id> --body '{"archived":false}'.

metabase card archive 1
metabase 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.

metabase dashboard list

metabase dashboard list
metabase dashboard list --json
metabase dashboard list --filter archived --json

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

metabase dashboard get <id>

metabase dashboard get 1
metabase dashboard get 1 --json
metabase 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.

metabase dashboard cards <id>

List the dashcards on a dashboard.

metabase dashboard cards 1
metabase dashboard cards 1 --json

metabase 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 | metabase dashboard create
metabase dashboard create --file dashboard.json
metabase dashboard create --body '{"name":"My Dashboard","collection_id":4}'
metabase 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. |

metabase 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 | metabase dashboard update 1
metabase dashboard update 1 --file patch.json
metabase dashboard update 1 --body '{"name":"renamed"}'
metabase dashboard update 1 --body '{"dashcards":[{"id":-1,"card_id":42,"row":0,"col":0,"size_x":12,"size_y":6}]}'

metabase 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.

metabase dashboard update-dashcard 1 5 --body '{"row":2,"col":0}'
metabase dashboard update-dashcard 1 5 --body '{"size_x":12,"size_y":4}'
cat patch.json | metabase 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.

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).

metabase snippet list

metabase snippet list
metabase snippet list --json
metabase snippet list --archived --json

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

metabase snippet get <id>

metabase snippet get 1
metabase snippet get 1 --json --full

metabase snippet create

cat snippet.json | metabase snippet create
metabase snippet create --file snippet.json
metabase 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).

metabase 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 | metabase snippet update 1
metabase snippet update 1 --file patch.json
metabase snippet update 1 --body '{"name":"renamed"}'
metabase snippet update 1 --body '{"archived":true}'

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

metabase snippet archive <id>

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

metabase snippet archive 1
metabase 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.

metabase segment list

metabase segment list
metabase segment list --json

metabase segment get <id>

metabase segment get 1
metabase segment get 1 --json --full

metabase segment create

cat segment.json | metabase segment create
metabase segment create --file segment.json
metabase 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 metabase query; pass --skip-validate to bypass.

metabase 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 | metabase segment update 1
metabase segment update 1 --file patch.json
metabase segment update 1 --body '{"name":"renamed","revision_message":"rename"}'
metabase 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. |

metabase segment archive <id>

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

metabase segment archive 1
metabase 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.

metabase measure list

metabase measure list
metabase measure list --json

metabase measure get <id>

metabase measure get 1
metabase measure get 1 --json --full

metabase measure create

cat measure.json | metabase measure create
metabase measure create --file measure.json
metabase 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 metabase query; pass --skip-validate to bypass.

metabase 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 | metabase measure update 1
metabase measure update 1 --file patch.json
metabase measure update 1 --body '{"name":"renamed","revision_message":"rename"}'
metabase 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. |

metabase measure archive <id>

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

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

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

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.

metabase collection list

metabase collection list
metabase collection list --json
metabase collection list --filter archived --json

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

metabase 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.

metabase collection get 4
metabase collection get root --json
metabase collection get trash --json
metabase collection get voo1If9y8Sld0lXej6xl0 --json
metabase 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.

metabase 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).

metabase collection items 4
metabase collection items root --json
metabase collection items 4 --models card,dashboard --json
metabase 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. |

metabase 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.

metabase collection tree
metabase collection tree --json

metabase 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 | metabase collection create
metabase collection create --file collection.json
metabase collection create --body '{"name":"My Collection","parent_id":4}'

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

Settings

Read and write Metabase instance settings via /api/setting. Listing all settings requires admin privileges; per-key reads/writes additionally enforce per-setting access. Setting values are always JSON — "main" is the string main, 42 is a number, null deletes the override and resets the value to its default.

metabase setting list

metabase setting list
metabase setting list --json --max-bytes 0

Returns a ListEnvelope of compact entries (key, value, is_env_setting, env_name). Pass --full for the full per-row payload (also includes description and default). The full payload can exceed the default --max-bytes cap; pass --max-bytes 0 to disable the cap.

metabase setting get <key>

metabase setting get site-name
metabase setting get remote-sync-branch --json

Returns { key, value } for a single setting. Settings whose stored value matches the default — or that come from an env var — surface as value: null.

metabase setting set <key> [value]

Set or delete a setting. The value is parsed strictly as JSON: pass '"main"' for the string main, true/42 for booleans/numbers, null to delete the stored override (resets to default).

metabase setting set remote-sync-branch '"main"'
metabase setting set anon-tracking-enabled true
echo '"main"' | metabase setting set remote-sync-branch
metabase setting set remote-sync-branch --file value.json
metabase setting set remote-sync-branch null

| Flag | Description | | --------------- | ------------------------------------------------------------------------ | | --file <path> | Read the JSON value from a file (alternative to the positional / stdin). |

Sources are resolved in this order: positional, --file, piped stdin. Provide exactly one; an unparseable value or a missing source fails fast with a ConfigError.

Search

metabase search [query]

Search Metabase content (cards, dashboards, collections, tables, …). Returns a ListEnvelope of compact search results by default; pass --detail full for the full per-row payload.

metabase search orders
metabase search --models card,dashboard --limit 10 --json
metabase search products --archived

| Flag | Description | | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | --models, -m | Comma-separated model filter: card,dataset,metric,dashboard,collection,database,table,segment,measure,snippet,document,action,transform,indexed-entity. | | --archived | Include archived items only. | | --limit | Max results to return (default 20). | | --table-db-id | Restrict to items on a given database id. | | --verified | Only verified content. |

Git Sync

Drive Metabase Enterprise Remote Sync (/api/ee/remote-sync) — import / export Metabase content against a configured git remote, inspect dirty state, and manage branches. All git-sync commands require an active EE token and superuser credentials.

metabase git-sync status

Roll up the current sync state in one call: configured branch, dirty flag, and the most recent sync task (or null if none has ever run).

metabase git-sync status
metabase git-sync status --json

metabase git-sync is-dirty

Boolean check for whether any synced collection has unsynced local changes.

metabase git-sync is-dirty --json

metabase git-sync has-remote-changes

Compare the latest version on the remote branch against the version Metabase last imported. Cached for a short TTL server-side; pass --force-refresh to bypass.

metabase git-sync has-remote-changes
metabase git-sync has-remote-changes --force-refresh --json

| Flag | Description | | ----------------- | --------------------------------------------------- | | --force-refresh | Bypass the in-memory cache and re-check the remote. |

metabase git-sync dirty

List every object that has unsynced local changes (compact list envelope; --full for the per-row payload).

metabase git-sync dirty
metabase git-sync dirty --json

metabase git-sync current-task

Fetch the most recent sync task. Renders { status: "idle" } when no task has ever run, otherwise the full task with its hydrated status.

metabase git-sync current-task
metabase git-sync current-task --json

metabase git-sync cancel-task

Cancel the currently running sync task. Fails with HTTP 400 if no task is running.

metabase git-sync cancel-task --json

metabase git-sync wait

Poll /current-task until it reaches a terminal status (successful, errored, cancelled, timed-out, conflict). Exits 0 on successful or cancelled; exits 1 on errored / timed-out / conflict. Returns immediately with { status: "idle" } if no task is running.

metabase git-sync wait
metabase git-sync wait --timeout 300000 --json

| Flag | Description | | ----------------- | --------------------------------------- | | --timeout <ms> | Polling timeout in ms (default 600000). | | --interval <ms> | Polling interval in ms (default 2000). |

metabase git-sync import

Import content from the configured git remote into Metabase (repo → Metabase). Auto-polls until the resulting task reaches a terminal status; pass --no-wait to return immediately after kickoff.

metabase git-sync import
metabase git-sync import --branch main --json
metabase git-sync import --force --no-wait

| Flag | Description | | ----------------------- | --------------------------------------------------------------------- | | --branch <name>, -b | Branch to import from (defaults to the remote-sync-branch setting). | | --force | Discard local Metabase-side dirty changes before importing (LOSSY). | | --wait / --no-wait | Poll until the task reaches a terminal status (default: wait). | | --timeout <ms> | Polling timeout in ms (default 600000). Used with --wait. | | --interval <ms> | Polling interval in ms (default 2000). Used with --wait. |

metabase git-sync export

Export Metabase changes back to the configured git remote (Metabase → repo). Auto-polls by default.

metabase git-sync export -m "update dashboards"
metabase git-sync export --branch main --json
metabase git-sync export --no-wait

| Flag | Description | | ----------------------- | ------------------------------------------------------------------- | | --branch <name>, -b | Branch to export to (defaults to the remote-sync-branch setting). | | --message <msg>, -m | Commit message for the export. | | --force | Force-push / overwrite the remote branch. | | --wait / --no-wait | Poll until the task reaches a terminal status (default: wait). | | --timeout <ms> | Polling timeout in ms (default 600000). Used with --wait. | | --interval <ms> | Polling interval in ms (default 2000). Used with --wait. |

metabase git-sync stash

Export the current Metabase state to a NEW branch on the remote and switch sync to it. Requires remote-sync-type to be read-write.

metabase git-sync stash --new-branch wip
metabase git-sync stash --new-branch wip -m "work in progress" --json

| Flag | Description | | ----------------------- | -------------------------------------------------------------- | | --new-branch <name> | Required. Branch to create and export to. | | --message <msg>, -m | Commit message (default Stashed from metabase CLI). | | --wait / --no-wait | Poll until the task reaches a terminal status (default: wait). | | --timeout <ms> | Polling timeout in ms. Used with --wait. | | --interval <ms> | Polling interval in ms. Used with --wait. |

metabase git-sync branches

List branches available on the configured git remote.

metabase git-sync branches --json

metabase git-sync create-branch <name>

Create a new branch on the git remote (from the last imported version) and switch sync to it.

metabase git-sync create-branch feat/dashboards
metabase git-sync create-branch feat/x --json

metabase git-sync add-collection <id>

Mark a collection as git-synced. The toggle cascades to every descendant by location prefix, so flagging a parent flags the whole subtree. Returns { success, task_id? }; task_id only appears when the toggle triggers a follow-up task (e.g. a finalization import after switching to read-only mode).

metabase git-sync add-collection 12
metabase git-sync add-collection 12 --json --profile prod

The server rejects toggles while remote-sync-type is read-only (the install default). Switch first with metabase setting set remote-sync-type '"read-write"'.

metabase git-sync remove-collection <id>

Unmark a collection as git-synced. Same cascade and same read-only precondition as add-collection.

metabase git-sync remove-collection 12
metabase git-sync remove-collection 12 --json --profile prod

Workspaces

CRUD on /api/ee/workspace-manager. Run against the workspace-manager parent instance.

metabase workspace list

metabase workspace list
metabase workspace list --json

metabase workspace create

metabase workspace create --name analytics
echo '{"name":"analytics"}' | metabase workspace create
metabase workspace create --file workspace.json

| Flag | Description | | --------------- | ------------------------------------------------------- | | --name <name> | Workspace name. Shortcut for --body '{"name":"<n>"}'. | | --body <json> | Inline JSON body. | | --file <path> | Path to JSON body file. |

metabase workspace database provision <workspace-id>

Provision a database into a workspace. The backend kicks off the work asynchronously and returns the workspace with the new entry in status: "provisioning". Pass --wait to poll until the entry reaches status: "provisioned" and surface the polled state instead of the initial response.

metabase workspace database provision 1 --database-id 5 --schemas analytics,github
metabase workspace database provision 1 --database-id 5 --schemas analytics --wait
metabase workspace database provision 1 --file provision.json

| Flag | Description | | -------------------- | -------------------------------------------------------------- | | --database-id <id> | Database id (used with --schemas). | | --schemas <csv> | Comma-separated input schemas (used with --database-id). | | --body <json> | Inline JSON body. | | --file <path> | Path to JSON body file. | | --wait | Poll until the database entry reaches status: "provisioned". | | --timeout <ms> | Polling timeout in ms (default 600000). Used with --wait. | | --interval <ms> | Polling interval in ms (default 2000). Used with --wait. |

metabase workspace database update <workspace-id> <db-id>

Update a workspace's provisioned database (server-side this is deprovision + provision). Body accepts only input — the database id comes from the URL.

metabase workspace database update 1 5 --schemas analytics,github
metabase workspace database update 1 5 --schemas analytics --wait
metabase workspace database update 1 5 --file update.json

| Flag | Description | | ----------------- | ----------------------------------------------------------------- | | --schemas <csv> | Comma-separated input schemas. Shortcut for body. | | --body <json> | Inline JSON body ({"input":[{"schema":"..."}]}). | | --file <path> | Path to JSON body file. | | --wait | Poll until the database entry returns to status: "provisioned". | | --timeout <ms> | Polling timeout in ms (default 600000). Used with --wait. | | --interval <ms> | Polling interval in ms (default 2000). Used with --wait. |

metabase workspace database deprovision <workspace-id> <db-id>

metabase workspace database deprovision 1 5 --yes
metabase workspace database deprovision 1 5 --yes --wait

| Flag | Description | | ----------------- | --------------------------------------------------------------------------------------------------------------------------------- | | --yes | Skip the interactive confirmation prompt. In non-TTY contexts the prompt is skipped automatically (kubectl/gh/docker convention). | | --wait | Poll until the database entry is removed from the workspace. | | --timeout <ms> | Polling timeout in ms (default 600000). Used with --wait. | | --interval <ms> | Polling interval in ms (default 2000). Used with --wait. |

Local runtime

These commands manage a Docker container that serves as the workspace's child Metabase instance. State lives in Docker labels and a named volume — there is no per-workspace local state directory. The container is named metabase-workspace-<id>; the app-db volume is metabase-workspace-<id>-appdb.

metabase workspace start <id>

metabase workspace start 1
metabase workspace start 1 --wait
metabase workspace start 1 --port 3100
metabase workspace start 1 --image metabase/metabase-dev:feature-workspaces-v2 --no-pull
metabase workspace start 1 --force
metabase workspace start 1 --repo /path/to/sync-repo --wait
metabase workspace start 1 --repo /path/to/sync-repo --repo-branch dev --repo-mode read-only

Resolves the parent via the active profile (or --profile/--url/--api-key) and the EE license via resolveLicenseToken (the same path metabase license set writes to). Refuses to start if the workspace has any database that isn't status: "provisioned".

The boot bundle (config.yml, credentials.json, optional metadata.json) is built in process memory and tar-streamed into the container's /mw-config/ directory through docker cp -; no host-disk artifact is created. The CLI generates a per-workspace admin user + API key, injects them into the YAML before shipping, and stores the same values in credentials.json for later retrieval via metabase workspace credentials. Once the child logs that it has read config.yml, the CLI scrubs the in-container copy (docker exec rm /mw-config/config.yml) so the warehouse credentials in details.password no longer linger; credentials.json stays.

By default start returns once the bundle has been consumed by the child (state: "starting"); pass --wait to also block until /api/health reports ready and the response reports state: "running".

When --repo <host-path> is passed, the CLI bind-mounts the host directory at /mnt/repo inside the container and injects three settings into the workspace's config.yml so the child boots already wired to the repo: remote-sync-url=file:///mnt/repo, remote-sync-branch=<branch> (defaults to the current branch of the host repo, read via git -C <path> symbolic-ref --short HEAD; override with --repo-branch), and remote-sync-type=<mode> (defaults to read-write; override with --repo-mode read-only, which also makes the bind mount read-only). The bind mount is set at container-create time only — to add or change it after the fact, run start --force again with the new flags. The host path must be an existing directory; the CLI does not create or git init it for you.

| Flag | Description | | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | --port <n> | Host port (default: 3000; auto-shifts up to 100 ports if taken). | | --image <ref> | Docker image (default: metabase/metabase-dev:feature-workspaces-v2). | | --wait | Block until /api/health is ready. Default: return as soon as consumed. | | --timeout <ms> | Per-phase readiness deadline (default: 240000). Covers post-create config consumption and (with --wait) the /api/health probe. | | --no-pull | Skip docker pull (useful if the image is already present). | | --no-metadata | Skip the warehouse metadata export. | | --force | If a container for this workspace already exists, remove it before starting. | | --repo <host-path> | Bind-mount a host directory at /mnt/repo and set remote-sync-url=file:///mnt/repo in config.yml. | | --repo-branch <name> | remote-sync-branch value (default: current branch of the host repo). | | --repo-mode <mode> | remote-sync-type: read-write (default) or read-only. Read-only also makes the bind mount read-only. |

metabase workspace stop <id>

metabase workspace stop 1
metabase workspace stop 1 --json

Stops the running container; no-ops if it's already exited or missing. Reports the prior state.

metabase workspace remove <id>

metabase workspace remove 1 --yes
metabase workspace remove 1 --keep-volume --yes

Stops and removes the container. By default, also removes the app-db volume — pass --keep-volume to preserve it across rebuilds. Does not affect the remote workspace on the parent.

| Flag | Description | | --------------- | --------------------------------------------------------------------------------------------------------------------------------- | | --yes | Skip the interactive confirmation prompt. In non-TTY contexts the prompt is skipped automatically (kubectl/gh/docker convention). | | --keep-volume | Preserve the app-db volume (metabase-workspace-<id>-appdb). |

metabase workspace logs <id>

metabase workspace logs 1
metabase workspace logs 1 --follow
metabase workspace logs 1 --tail 500

Passthrough to docker logs. Output streams directly to your terminal; Ctrl-C terminates a follow.

| Flag | Description | | -------------- | --------------------------------------------- | | --follow, -f | Stream indefinitely. | | --tail <n> | Lines from the end of the logs (default 200). |

metabase workspace url <id>

metabase workspace url 1
metabase workspace url 1 --json

Prints http://localhost:<port> for the workspace's container. Reads the host port from the container's com.metabase.workspace.host-port label.

metabase workspace credentials <id>

metabase workspace credentials 1
metabase workspace credentials 1 --json

Reads the workspace child's admin credentials (email, password, admin API key) from /mw-config/credentials.json inside the container. The file is written by workspace start from CLI-generated, per-workspace values; the same values are injected into config.yml's :users and :api-keys sections so they take effect on the child's first boot. Works against running and stopped containers (uses docker cp); errors clearly if no container exists for the given workspace id. Removing the container destroys the file — recover by workspace start <id> --force.

metabase workspace ps

metabase workspace ps
metabase workspace ps --json

Lists every container that carries the com.metabase.workspace.id label, running or stopped. The --json envelope is the canonical agent-facing shape and contains only workspace_id, workspace_name, state, and url; --full --json emits the wider record (image, profile, parent URL, container name, status string, host port).

Instance setup

Operations against a workspace-instance Metabase. The setup wizard and API key creation are distinct endpoints — there is no shared body schema.

metabase setup

Complete the initial setup wizard (POST /api/setup). The body must include the setup token, the default user, and the prefs block (with site_name).

cat setup.json | metabase setup
metabase setup --file setup.json
metabase setup --body '{"token":"<setup-token>","user":{"email":"[email protected]","password":"..."},"prefs":{"site_name":"Acme"}}'

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

metabase api-key create

Create a new API key (POST /api/api-key). The unmasked key is returned on creation only; capture it from the output.

metabase api-key create --name deploy-bot --group-id 2
echo '{"name":"k","group_id":2}' | metabase api-key create
metabase api-key create --file key.json

| Flag | Description | | ----------------- | ----------------------------------------- | | --name <name> | API key name (used with --group-id). | | --group-id <id> | Permission group id (used with --name). | | --body <json> | Inline JSON body. | | --file <path> | Path to JSON body file. |

Agent helpers

Endpoints commonly used by agents driving the instance. card query and transform run are documented in their own sections; the helper below covers entity-id translation.

metabase eid translate

Translate string entity ids (EIDs) to numeric ids (POST /api/eid-translation/translate).

metabase eid translate --model card --eids abc123XYZ,def456ABC
metabase eid translate --file translate.json
metabase eid translate --body '{"entity_ids":{"card":["abc123XYZ"]}}'

| Flag | Description