Skip to content

Rank #4 of 5 in Serverless & Developer Databases

PlanetScale logo

PlanetScale, Inc. · commercial

npm 312k/wknpm/wk -35.3k

Showcase

PlanetScale homepage screenshot
homepage · captured Sep 2026 · view live ↗
PlanetScale docs screenshot
docs · captured Sep 2026 · view live ↗

Try itExperimental

See what an agent can do with PlanetScale before you ever sign up. Pick a story: recorded sessions replay real probe-harness transcripts; sandboxed self-drive sessions are designed and gated (docs/TRY-IT.md).

$pscale --skillrecorded session — replayed, not live
recorded 2026-09-06 · exit 0 · captured verbatim by our probe harness, secrets redacted

Verified integrations

Connections to other tracked products — hover a chip for the verbatim evidence quote behind it.

By theme — the product's score on each story themeBy theme

Agent workflows — stories about agent workflows in this arenaAgent workflowsevidence →

Stories about agent workflows in this arena

36.9/100

Agenticness — how well agents can access and operate the productAgenticnessevidence →

How well agents can access and operate the product

48.9/100

Automation depth — how much of the product can run unattendedAutomation depthevidence →

How much of the product can run unattended

32.8/100

Branching workflows — stories about branching workflows in this arenaBranching workflowsevidence →

Stories about branching workflows in this arena

65.0/100

Connectivity pooling — stories about connectivity pooling in this arenaConnectivity poolingevidence →

Stories about connectivity pooling in this arena

22.0/100

Data capabilities — stories about data capabilities in this arenaData capabilitiesevidence →

Stories about data capabilities in this arena

5.1/100

Local dev — stories about local dev in this arenaLocal devevidence →

Stories about local dev in this arena

16.8/100

Migrations schema — stories about migrations schema in this arenaMigrations schemaevidence →

Stories about migrations schema in this arena

34.3/100

Openness — open source, data portability, and self-hosting storiesOpennessevidence →

Open source, data portability, and self-hosting stories

7.2/100

Operations insights — stories about operations insights in this arenaOperations insightsevidence →

Stories about operations insights in this arena

60.0/100

Pricing plans — plan structure and value — what each tier costs and what it unlocksPricing plansevidence →

Plan structure and value — what each tier costs and what it unlocks

14.4/100

Privacy posture — data-handling and privacy storiesPrivacy postureevidence →

Data-handling and privacy stories

8.0/100

Reliability recovery — stories about reliability recovery in this arenaReliability recoveryevidence →

Stories about reliability recovery in this arena

74.3/100

Serverless scale — stories about serverless scale in this arenaServerless scaleevidence →

Stories about serverless scale in this arena

26.3/100

Story verdicts — every judged story with its evidenceStory verdicts

What’s free: 1 free · 2 paid · 0 enterprise · 34 not stated in evidence

?

Sorted by importance (agentic first) (high → low) · 56/56 stories · click a row’s chevron for the rationale and evidence

Connect an agent via an official MCP server G

Agent access

ai-native userAgenticness — how well agents can access and operate the productAgenticness3full9/10T

Drive the product through a documented public API G

Agent access

ai-native userAgenticness — how well agents can access and operate the productAgenticness3partial6/10T

Delegate tasks to a built-in AI assistant inside the product G

Agentic features

ai-native userAgenticness — how well agents can access and operate the productAgenticness3partial4/10T

Plug MCP servers into this product so it can use their tools G

Agent access

ai-native userAgenticness — how well agents can access and operate the productAgenticness3n/a0/10

Point an agent at llms.txt or agent-oriented docs G

Agent access

ai-native userAgenticness — how well agents can access and operate the productAgenticness2full9/10T

Use an official CLI G

Agent access

ai-native userAgenticness — how well agents can access and operate the productAgenticness2full9/10T

Get AI-generated insights and suggestions from my data inside the product G

Agentic features

ai-native userAgenticness — how well agents can access and operate the productAgenticness2full8/10T

Run the product headlessly / in CI for automation G

Agent access

ai-native userAgenticness — how well agents can access and operate the productAgenticness2full8/10T

Subscribe to events via webhooks G

Agent access

ai-native userAgenticness — how well agents can access and operate the productAgenticness2full8/10C

Set up automations that run autonomously in the background G

Agentic features

ai-native userAgenticness — how well agents can access and operate the productAgenticness2full7/10C

Issue scoped/least-privilege API credentials for an agent G

Agent access

ai-native userAgenticness — how well agents can access and operate the productAgenticness2partial6/10T

Operate the product with natural-language commands G

Agentic features

ai-native userAgenticness — how well agents can access and operate the productAgenticness2partial6/10T

Download a machine-readable API spec (OpenAPI or equivalent) G

Api quality

ai-native userAgenticness — how well agents can access and operate the productAgenticness2none0/10

Explore an interactive API reference with runnable examples G

Api quality

ai-native userAgenticness — how well agents can access and operate the productAgenticness2none0/10

Rely on versioned APIs with a documented deprecation policy G

Api quality

ai-native userAgenticness — how well agents can access and operate the productAgenticness2none0/10

Build against official SDKs G

Agent access

ai-native userAgenticness — how well agents can access and operate the productAgenticness2noneuntestednone yet

Test against a sandbox environment without touching production data G

Api quality

ai-native userAgenticness — how well agents can access and operate the productAgenticness1full9/10T

Create an instant copy-on-write branch of my database — schema and data — to develop and test against production-shaped data C

Branching

developerBranching workflows — stories about branching workflows in this arenaBranching workflows3full9/10X

Restore or branch the database to any point in time within the retention window to recover from bad writes C

Recovery

platform-engineerReliability recovery — stories about reliability recovery in this arenaReliability recovery3full8/10C

My coding agent can provision a database end to end — create it, fetch the connection string, apply schema, and run queries — through the API, CLI, or MCP without touching the dashboard C

Agent ops

ai-native userAgent workflows — stories about agent workflows in this arenaAgent workflows3partial7/10T

Ship schema changes safely — online DDL, deploy requests, or branch-and-merge workflows — without locking or breaking production C

Migrations

developerMigrations schema — stories about migrations schema in this arenaMigrations schema3partial7/10X

Create a ready-to-connect database in seconds through the CLI or API without capacity planning C

Provisioning

developerServerless scale — stories about serverless scale in this arenaServerless scale3partial6/10X

Define rules that trigger actions automatically on events G

ai-native userAutomation depth — how much of the product can run unattendedAutomation depth3partial4/10C

I get real engine compatibility — the Postgres, MySQL, or SQLite dialect and extensions my existing code expects — not a lookalike subset C

Compatibility

developerData capabilities — stories about data capabilities in this arenaData capabilities3disputed4/10D

Published per-unit pricing for compute, storage, and traffic lets me predict my bill before committing G

Pricing

founderPricing plans — plan structure and value — what each tier costs and what it unlocksPricing plans3partialfree4/10X

Idle databases scale to zero so prototypes and side projects cost nothing while the data stays durable C

Elasticity

founderServerless scale — stories about serverless scale in this arenaServerless scale3partialpaid3/10X

Export all of my data in open formats and leave G

ai-native userOpenness — open source, data portability, and self-hosting storiesOpenness3none0/10

Run the same engine locally and keylessly — no account or cloud dependency — for offline development and CI tests G

Local loop

developerLocal dev — stories about local dev in this arenaLocal dev3none0/10

Self-host the core product G

ai-native userOpenness — open source, data portability, and self-hosting storiesOpenness3none0/10

Prevent my data from being used to train AI models G

ai-native userPrivacy posture — data-handling and privacy storiesPrivacy posture3n/auntestednone yet

See slow queries, index recommendations, and performance metrics in a built-in insights view C

Insights

platform-engineerOperations insights — stories about operations insights in this arenaOperations insights2full9/10X

Add read replicas and rely on documented high-availability and failover behavior C

Availability

platform-engineerReliability recovery — stories about reliability recovery in this arenaReliability recovery2full7/10X

Automatic backups run on a schedule I can see and configure, and restores are self-serve C

Recovery

founderReliability recovery — stories about reliability recovery in this arenaReliability recovery2full7/10C

Create and tear down per-pull-request database branches automatically from CI or my deploy platform C

Branching

platform-engineerBranching workflows — stories about branching workflows in this arenaBranching workflows2partial7/10T

The official CLI covers my daily loop — create, connect, shell into, and inspect databases — without opening the dashboard C

Local loop

developerLocal dev — stories about local dev in this arenaLocal dev2partial7/10T

A built-in connection pooler handles thousands of concurrent connections without me operating my own pgbouncer or proxy C

Pooling

platform-engineerConnectivity pooling — stories about connectivity pooling in this arenaConnectivity pooling2partial6/10C

An agent can run SQL and schema operations through scoped tools that distinguish read-only from destructive actions, so I can safely delegate database work C

Agent ops

ai-native userAgent workflows — stories about agent workflows in this arenaAgent workflows2partial6/10T

Do everything through the API that I can do in the UI G

ai-native userOpenness — open source, data portability, and self-hosting storiesOpenness2partial6/10T

Cheaply create thousands of isolated databases — one per agent, tenant, or preview — and manage the fleet programmatically C

Agent ops

ai-native userAgent workflows — stories about agent workflows in this arenaAgent workflows2partialpaid5/10T

Perform bulk operations across many items at once G

ai-native userAutomation depth — how much of the product can run unattendedAutomation depth2partial5/10T

Place data or replicas in regions close to my users to keep read latency low worldwide C

Latency

developerConnectivity pooling — stories about connectivity pooling in this arenaConnectivity pooling2partial5/10C

Schedule recurring jobs or workflows G

ai-native userAutomation depth — how much of the product can run unattendedAutomation depth2partial5/10C

Choose where my data is stored (region/residency) G

ai-native userPrivacy posture — data-handling and privacy storiesPrivacy posture2partial4/10C

Compute autoscales up and down with load automatically, without manual resizes or downtime C

Elasticity

platform-engineerServerless scale — stories about serverless scale in this arenaServerless scale2partial4/10X

A genuinely usable free tier lets me run real prototypes before paying G

Pricing

founderPricing plans — plan structure and value — what each tier costs and what it unlocksPricing plans2none0/10

Read the product's source under an open license G

ai-native userOpenness — open source, data portability, and self-hosting storiesOpenness2none0/10

Control data retention and deletion G

ai-native userPrivacy posture — data-handling and privacy storiesPrivacy posture2noneuntestednone yet

Opt out of telemetry and usage tracking G

ai-native userPrivacy posture — data-handling and privacy storiesPrivacy posture2noneuntestednone yet

Query over HTTP or WebSockets from serverless and edge functions with an official driver built for short-lived connections C

Drivers

developerConnectivity pooling — stories about connectivity pooling in this arenaConnectivity pooling2noneuntestednone yet

Run heavy analytical aggregations over large tables fast enough for dashboards without exporting to a separate warehouse C

Analytics

developerData capabilities — stories about data capabilities in this arenaData capabilities2noneuntestednone yet

Store embeddings and run vector similarity search natively without adding a separate vector database C

Ai data

developerData capabilities — stories about data capabilities in this arenaData capabilities2noneuntestednone yet

The database works out of the box with my ORM and framework (Prisma, Drizzle, Django, Rails) with documented guides C

Integrations

developerMigrations schema — stories about migrations schema in this arenaMigrations schema2noneuntestednone yet

Import an existing production database with minimal downtime using a documented migration path C

Migrations

developerMigrations schema — stories about migrations schema in this arenaMigrations schema1full8/10X

Version, review, and roll back my automations G

ai-native userAutomation depth — how much of the product can run unattendedAutomation depth1full7/10C

Reset a branch from its parent or restore it to an earlier state without rebuilding from a dump C

Branching

developerBranching workflows — stories about branching workflows in this arenaBranching workflows1partial6/10C

Set spend caps or usage alerts so a runaway query or traffic spike cannot produce a surprise bill G

Cost controls

founderOperations insights — stories about operations insights in this arenaOperations insights1none0/10

Opportunities — the stories that would move this product's scores, from its own judged verdictsOpportunitiestop 8 of 38 stories with headroom

What would move PlanetScale’s scores — derived from its own judged verdicts, biggest headroom first. Each line quotes what the judge found missing; shipping it (or evidencing it publicly) is the fix.

  1. Local dev — stories about local dev in this arenaRun the same engine locally and keylessly — no account or cloud dependency — for offline development and CI tests

    nonemoves PA Scoreimpact 30

    PlanetScale is a cloud-hosted database platform; there is no evidence of a downloadable/local engine that runs keylessly offline.

  2. Openness — open source, data portability, and self-hosting storiesExport all of my data in open formats and leave

    nonemoves PA Scoreimpact 30

    The evidence pack shows no vendor documentation of a bulk data-export feature or open-format dump/leave workflow (only an import tool for MySQL/MariaDB is documented).

  3. Openness — open source, data portability, and self-hosting storiesSelf-host the core product

    nonemoves PA Scoreimpact 30

    PlanetScale is a fully-hosted cloud database platform with no documented self-host/on-prem option; community evidence explicitly confirms it cannot be installed locally or run offline and is not open source ('Can I install this locally?...

  4. Agenticness — how well agents can access and operate the productBuild against official SDKs

    nonemoves agent-readyimpact 30

    Missing: any documentation of official SDKs/client libraries (Node, Go, Python, etc.), API reference tied to those SDKs, or independent confirmation of their existence.

  5. Agenticness — how well agents can access and operate the productExplore an interactive API reference with runnable examples

    nonemoves API qualityimpact 30

    Probes explicitly show no OpenAPI/interactive API reference exists (docs.md 404, all openapi.json candidate paths 404), and no evidence pack item describes a runnable-example API console; only static CLI/API docs and changelog entries are present.

  6. Agenticness — how well agents can access and operate the productDownload a machine-readable API spec (OpenAPI or equivalent)

    nonemoves API qualityimpact 30

    The probe explicitly checked for an OpenAPI/Swagger spec at all standard candidate paths and found only 404s, with no other evidence of a published machine-readable API spec.

  7. Agenticness — how well agents can access and operate the productRely on versioned APIs with a documented deprecation policy

    nonemoves API qualityimpact 30

    No evidence pack items mention API versioning scheme or a documented deprecation policy; probes for OpenAPI specs return 404s and changelog entries are feature announcements, not API-version/deprecation documentation.

  8. Agenticness — how well agents can access and operate the productDelegate tasks to a built-in AI assistant inside the product

    partialq4/10moves Built-in AIimpact 27

    Missing: a first-party in-app chat/assistant UI, independent hands-on confirmation of the self-improving-database feature actually running end-to-end, and clarity that it's not just MCP-enabled third-party agents.

Showing the top 8 of 38 — every none/partial verdict in the story verdicts table is headroom.

Think a verdict is wrong? Every verdicts-table row has a Flag link — see the methodology.

Coverage map — which docs area, API section, or community source covers which judged storiesCoverage map5 surfaces · 38 covered stories

Where the cited evidence behind each covered verdict came from — the same citations the verdicts table shows, no extra judging.

docs34 stories

Probe proofs — replayable recordings from the probe harnessProbe proofs

Replayable recordings from our probe harness — see the Prove-It protocol to submit one.

$pscale --skillreproduced
$ pscale --skill
---
name: pscale-cli
description: "Automate PlanetScale with the pscale CLI. Use when the user asks to run pscale commands or manage PlanetScale databases, branches, deploy requests, or SQL from scripts or agents. Always pass --format json."
---

# PlanetScale CLI — agent guide

For **any** automated agent or script using `pscale`. Always pass **`--format json`**. Substitute placeholders from the user's request or from prior command output (`org list`, `database list`, `branch list`).

If you only have the installed `pscale` binary and this guide is not already in your context, load it and check auth:

```bash
pscale --skill
pscale auth check --format json
```

Use direct CLI automation for shell commands and scripts. Use the hosted PlanetScale MCP server for MCP clients.

This file documents **how to invoke `pscale`**. For database assessment, safety review, and operational workflows, install the [PlanetScale skills pack](https://github.com/planetscale/skills) (`14-pscale-cli-automation` covers CLI automation; `00-safe-orchestrator` runs the full review). In application repositories, add a separate **project** `AGENTS.md` with org, database, branch, and approval rules (see skill `09-mcp-agent-operating-model` in that repo).

## Public repository safety

This repository is public. Do not include internal or sensitive information in commits, commit messages, pull request titles, or pull request descriptions. Do not name or link private repositories, internal issues or pull requests, Slack conversations, customer details, credentials, or private infrastructure. Include references only when the referenced resource is public.

## Concepts

PlanetScale is a serverless database platform for **MySQL** (via Vitess) and **PostgreSQL**. Resources are namespaced: an **organization** (org) owns **databases**, and each database contains **branches** — isolated copies of schema (and, for Postgres, data) that work like git branches. The default branch is typically `main` (production). Most commands target a database + branch and take `--org` to say which organization they belong to. Throughout this guide, `<org>`, `<database>`, and `<branch>` are placeholders for those names — pick a branch with `"ready": true` from `branch list`.

On Vitess/MySQL, schema changes ship via **deploy requests**: online, non-blocking migrations you review and then deploy.

Many commands are engine-specific, and some operations use different commands per engine. Schema changes: Vitess/MySQL uses `deploy-request`; Postgres branches apply DDL directly. Access: Vitess/MySQL uses `password`; Postgres uses `role`. Resize: Vitess/MySQL uses `[redacted]space resize`; Postgres uses `branch resize`. Vitess/MySQL-only: `deploy-request`, `[redacted]space`, `workflow`, `connect`, `password`. Postgres-only: `role`, `traffic-control`, branch `switchover`/`maintenance`/`parameters`, and `import d1`. The rest (`database`, `branch`, `sql`, `shell`, `insights`, `metrics`, `backup`, `org`, `auth`, `api`) work on both.

When a database is "weird" (slow, erroring, locked, bloated):

- **`insights`** — historical analysis computed from production traffic. Start here.
- **`inspect`** — live, point-in-time state over a direct connection (locks, in-flight queries, sizes).
- **`metrics`** — time-series for latency, throughput, connections.

See "Diagnostics: insights + inspect" below for the full commands.

## Flag placement

- **`--org`** is a flag on resource subcommands (`database`, `branch`, `sql`, `api`, …). It is **not** on root `pscale` — `pscale --org …` fails.
- **`--format json`** is a global flag. It can go on `pscale` or on the subcommand.
- Commands with positional args (`sql`, `branch list`, …): put **positionals first**, then flags.

```bash
# Correct
pscale auth check --format json
pscale org list --format json
pscale database list --org <org> --format json
pscale branch list <database> --org <org> --format json
pscale sql <database> <branch> --org <org> --format json --query "SELECT 1"

# Also valid — global --format
pscale --format json database list --org <org>

# Wrong — unknown flag: --org
pscale --org <org> database list --format json
```

## Workflow

1. **Guide** — if this file is not already in your context, load the skill:

   ```bash
   pscale --skill
   ```

2. **Auth** — check before anything else:

   ```bash
   pscale auth check --format json
   ```

   `"status": "ok"` and `"authenticated": true` with no blocking `issues` means proceed. `"status": "action_required"` exits non-zero — log in, pick an org, or fix credentials (see `issues` and `next_steps`).

3. **Login** (when not authenticated):

   ```bash
   pscale auth login --format json
   ```

   Pending JSON is written to **stderr** while waiting; **stdout** has a single final JSON object when login completes (`status: ok` or `action_required` if org setup fails after credentials are saved). Fields include `verification_url`, `user_code`, and `browser_opened`. Open `verification_url` manually if the browser does not open. Do not retry login in a loop without browser access.

4. **Organization** — use `"organization"` from `auth check`, ask the user, or list orgs:

   ```bash
   pscale org list --format json
   ```

   Pass `--org <org>` on resource commands (`database`, `branch`, `sql`, `api`, …). Not on `org list`.

   Organization settings:

   ```bash
   pscale org update --org <org> --format json --billing-email billing@example.com
   pscale org update --org <org> --format json --idp-managed-roles=false
   pscale org update --org <org> --format json --idp-sso-managed-roles=true
   pscale org update --org <org> --format json --spend-alert=true --spend-alert-amount 2500
   pscale org update --org <org> --format json --spend-alert=false
   ```

   Organization members (email or the USER_ID from list):

   ```bash
   pscale org member list --org <org> --format json
   pscale org member list --org <org> --format json --page 2 --per-page 25
   pscale org member show user@example.com --org <org> --format json
   pscale org member update user@example.com --org <org> --format json --role member
   pscale org member remove user@example.com --org <org> --format json --force
   ```

   Only org admins can change another member's role or remove someone else. Nobody can change their own role. `--role` is `admin`, `member`, or `analyst`.

   Organization SSO (admin session, or a [redacted] with `manage_sso`). Disable, directory disable, and domain delete need `--force` in JSON. Success JSON includes `next_steps`. Configure, directory enable, and domain verify return `portal_url` and `browser_opened`; if `browser_opened` is false, open `portal_url`. `enable` JSON includes `domain_verification_url` when present. `domain verify` does not return a domain id. Prefer `domain verify --wait`: it prints the portal URL, polls `domain list` until a new domain id appears, then polls `domain show` until that domain is verified or failed. Without `--wait`, open `portal_url`, then `domain list` and `domain show <domain-id>` to check state. `--idp-sso-managed-roles` is for SSO profile roles; `--idp-managed-roles` is for directory sync roles. They are mutually exclusive: enabling one turns the other off, and enabling both in one call is rejected.

   ```bash
   pscale org sso show --org <org> --format json
   pscale org sso enable --org <org> --format json
   pscale org sso configure --org <org> --format json
   pscale org sso disable --org <org> --format json --force
   pscale org sso directory enable --org <org> --format json
   pscale org sso directory disable --org <org> --format json --force
   pscale org sso domain list --org <org> --format json
   pscale org sso domain show <domain-id> --org <org> --format json
   pscale org sso domain verify --org <org> --format json
   pscale org sso domain verify --org <org> --format json --wait
   pscale org sso domain delete <domain-id> --org <org> --format json --force
   ```

Organization teams and team members:

```bash
pscale org team list --org <org> --format json
pscale org team list --org <org> --format json --page 2 --per-page 25
pscale org team show <team-id-or-name> --org <org> --format json
pscale org team create --name <name> --description <description> --org <org> --format json
pscale org team update <team> --name <name> --description <description> --org <org> --format json
pscale org team delete <team> --org <org> --format json --force
pscale org team member list <team> --org <org> --format json
pscale org team member list <team> --org <org> --format json --page 2 --per-page 25
pscale org team member add <team> <email-or-id> --org <org> --format json
pscale org team member remove <team> <email-or-id> --org <org> --format json --force
```

Team identifiers may be IDs, names, or slugs. Team and membership deletion requires approval before using `--force`. SSO/directory-managed teams cannot be updated, deleted, or have members added or removed through the API.

5. **Discover resources** before SQL:

   ```bash
   pscale database list --org <org> --format json
   pscale database regions list <database> --org <org> --format json
   pscale database read-only-regions list <database> --org <org> --format json
   pscale branch list <database> --org <org> --format json
   ```

   `database regions list` returns the regions available to that database for
   its engine. `database read-only-regions list` returns configured Vitess
   read-only regions for the database's default branch.

6. **Query** (read-only default):

   ```bash
   pscale sql <database> <branch> --org <org> --format json --query "SELECT 1"
   ```

## Cloudflare-billed databases

To create a database billed to a Cloudflare account, Cloudflare must mint an HMAC billing proof. Pass the JSON proof directly:

```bash
pscale database create <database> --org <org> --format json \
  --cloudflare-billing '{"account_id":"<cloudflare_account_id>","timestamp":"<unix_timestamp>","signature":"<hmac_hex>"}'
```

For automation, pass `@-` to read the proof from stdin so the signature is not exposed in process arguments:

```bash
printf '%s' "$CLOUDFLARE_BILLING_JSON" | \
  pscale database create <database> --org <org> --format json --cloudflare-billing @-
```

The CLI does not mint the signature. The JSON object must contain non-empty `account_id`, `timestamp`, and `signature` strings.

## Flags

| Flag | Purpose |
|------|---------|
| `--format json` | JSON on stdout |
| `--org <org>` | Organization (on resource subcommands only) |
| `--api-url` | Non-production API base URL — pass on every command when not using production |
| `--cloudflare-billing` | Cloudflare billing proof JSON on `database create`; pass `@-` to read it from stdin |

## JSON errors

With `--format json`, any command that fails prints exactly one JSON envelope on **stdout**:

```json
{
  "status": "error",
  "error": "human-readable message",
  "issues": [{ "code": "NOT_FOUND", "message": "human-readable message" }],
  "next_steps": ["pscale org list --format json", "pscale database list --org <org> --format json"]
}
```

- `status` is `"error"` or `"action_required"`. `action_required` means an agent can recover by following `next_steps` (log in, ask the user for approval, fix the invocation). Exit code is `1` for `action_required` and `2` for `error`.
- `issues[].code` is stable and machine-readable; branch on it, not on message text.
- `next_steps` are concrete commands or instructions, ordered by likelihood.

Some commands add fields to this envelope (for example `query_kind` on destructive SQL or `migration_id` on imports) but `status`, `issues`, and `next_steps` are always present on failure.

| Code | Meaning |
|------|---------|
| `NO_AUTH` | Not authenticated or [redacted] expired; run `pscale auth login --format json` |
| `AUTH_INVALID` | Stored credentials rejected by the API; log in again |
| `SERVICE_[redacted]_INVALID` | Service [redacted] id/secret rejected; verify the values |
| `NO_ORG` | Authenticated but no organization configured |
| `INVALID_FLAG_PLACEMENT` | `--org` was passed on `pscale` root; move it to the subcommand |
| `INVALID_USAGE` | Missing arguments or required flags; the message names them |
| `UNKNOWN_COMMAND` | Command does not exist; check `pscale --help` |
| `UNKNOWN_FLAG` | Flag does not exist on this command; check `--help` |
| `TTY_REQUIRED` | Command needs an interactive terminal; use the JSON alternative in `next_steps` |
| `CONFIRMATION_REQUIRED` | Destructive or gated action; ask the user, then re-run with `--force` |
| `DESTRUCTIVE_SQL` | Query would delete data or schema; ask the user, then re-run with `--force` |
| `NOT_FOUND` | Org, database, branch, or resource does not exist; run the discovery commands |
| `NETWORK_ERROR` | Transport-level failure; check connectivity and `--api-url`, then retry |
| `COMMAND_FAILED` | Unclassified failure; read `error` and rule out auth first |

## Authentication

`pscale auth login` stores credentials in the OS [redacted]chain; agents on the same machine reuse them.

Headless / CI: pass `--service-[redacted]-id` and `--service-[redacted]` on the subcommand that needs auth.

## SQL

Non-interactive queries. Default **`--role` is `reader`** (unlike `pscale shell`, which defaults to admin). Use `pscale shell` for interactive sessions.

```bash
# Read (default)
pscale sql <database> <branch> --org <org> --format json --query "SELECT 1"

# Read from replica
pscale sql <database> <branch> --org <org> --format json --replica --query "SELECT 1"

# PostgreSQL — optional --dbname (default postgres)
pscale sql <database> <branch> --org <org> --format json --query "SELECT 1"

# MySQL multi-[redacted]space — optional --[redacted]space (default @primary)
pscale sql <database> <branch> --org <org> --format json --[redacted]space <[redacted]space> --query "SELECT 1"
```

| Flag | Purpose |
|------|---------|
| `--role` | `reader` (default), `writer`, `readwriter`, `admin` — same names as `pscale shell` |
| `--replica` | Route reads to replicas |
| `--dbname` | PostgreSQL database name (default `postgres`) |
| `--[redacted]space` | MySQL [redacted]space (default `@primary`); may include a shard and tablet type: `my[redacted]space/-80`, `my[redacted]space/-80@replica` |
| `--force` | Allow destructive SQL after explicit user approval |

**`--role` by engine** (same as `pscale shell`):

| `--role` | MySQL (Vitess) | PostgreSQL |
|----------|----------------|------------|
| `reader` | Branch password, reader role | Ephemeral role inheriting `pg_read_all_data` |
| `writer` | Branch password, writer role | Role inheriting `pg_write_all_data` |
| `readwriter` | Branch password, readwriter role | Role inheriting read + write |
| `admin` | Branch password, admin role | Role inheriting `postgres` |

### Destructive SQL

`DELETE`, `DROP`, and `TRUNCATE` anywhere in a query are blocked by default (word match, not substring — `deleted_at` is fine). Returns `"status": "action_required"` with `"query_kind": "destructive"`.

1. Ask the user to approve the query.
2. Re-run with `--force` only after they approve:

```bash
pscale sql <database> <branch> --org <org> --format json --force --query "DELETE FROM ..."
```

Never use `--force` without explicit user approval.

### SQL JSON

Success: `status`, `database`, `branch`, `kind` (`mysql` or `postgresql`), `role`, `row_count`, `columns`, `rows`; `replica` when `--replica` was used.

MySQL may return synthetic column names (e.g. `:vtg1 /* INT64 */`). PostgreSQL may use names like `?column?`.

Error: one JSON object on stdout with `status: "error"`, `error`, `issues`, and `next_steps` (see JSON errors above).

Destructive SQL without `--force`: `status: "action_required"`, `query_kind: "destructive"`, `issues`, and `next_steps` (includes `--force` retry command).

## Metrics

Query historical or current branch metrics through the public metrics API:

```bash
pscale metrics show <database> <branch> --org <org> --format json --metric queries --metric latency_p99 --period 1h
pscale metrics instant <database> <branch> --org <org> --format json --metric planetscale_volume_usage_percentage
pscale metrics report <database> <branch> --org <org> --format json --period 1d
pscale metrics queries <database> <branch> --org <org> --format json --metric latency_p99 --fingerprint <fingerprint> --[redacted]space <[redacted]space> --period 1h
pscale metrics tables <database> <branch> --org <org> --format json
pscale metrics [redacted]space-tables <database> <branch> --org <org> --format json
pscale metrics tablets <database> <branch> --org <org> --format json --metric replication_lag --period 1h
pscale metrics tablets instant <database> <branch> --org <org> --format json --metric replication_lag
pscale metrics tags <database> <branch> --org <org> --format json --metric queries --tag-set Busername=alice --tag-set Busername=bob --period 1h
```

- `--metric` is required for series and instant commands and may be repeated or comma-separated.
- Specialized query, tablet, and tag metrics accept the filters exposed by their API endpoints. `--metric` and `--query-id` may be repeated or comma-separated. `--tag-set` is repeated for independent series; comma-separate [redacted]s inside one set (`Busername=alice,Senv=production`). Tag [redacted]s need the Insights type prefix from `insights tags`.
- `metrics queries` requires a query selector. Use `--fingerprint` with `--[redacted]space`, or `--query-id` as `<fingerprint>-<[redacted]space>`. The short `id` from `insights queries` is not a query pattern ID.
- `metrics tags` requires at least one `--tag-set`. `--budget-id` and `--rule-id` apply only to the `traffic_control_warnings` and `traffic_control_throttled` metrics.
- `metrics tablets --workflow` applies only to `--metric vreplication_lag` and must name an existing workflow on the branch.
- Filters that match nothing return zero-filled series rather than an empty response, so check the point values, not the series count.
- `metrics tables` and `metrics [redacted]space-tables` preserve the untyped storage-metrics API response in JSON.
- `metrics report` detects whether the database uses MySQL or PostgreSQL and queries a curated set of performance sections. It supports `--period`, custom `--from`/`--to` ranges, and `--steps`; JSON returns a composite report and CSV includes the section name on each row.
- Historical queries support `--period`, or a custom `--from`/`--to` ISO 8601 range, plus `--steps` and dimension filters such as `--tablet-type`, `--[redacted]space`, `--shard`, `--role`, `--pod`, and `--pods`.
- JSON preserves the API response: historical results contain `start_date`, `end_date`, `interval`, and `series`; each series contains `metric`, `label`, `labels`, and `[Unix timestamp, value]` points. Instant results contain current values grouped by their dimensions.
- Human output summarizes each historical series with latest/min/average/max values and a sparkline. CSV flattens historical samples or instant values to one row each.

## Diagnostics: insights + inspect

Two complementary read-only surfaces. When diagnosing database health or performance, **check both** — they see different things.

**`pscale insights`** — server-side analysis computed from production traffic (works even when you can't or don't want to connect to the database):

```bash
pscale insights queries <database> <branch> --org <org> --format json --sort totalTime   # top queries; sorts: totalTime, count, p99Latency, rowsRead, rowsReadPerReturned, errorCount, ...
pscale insights queries samples <database> <branch> <fingerprint> --org <org> --format json --[redacted]space <[redacted]space>  # recent executions; [redacted]space from queries list
pscale insights queries traffic-budgets <database> <branch> <fingerprint> --org <org> --format json --[redacted]space <[redacted]space> --page <n> --per-page <n>  # traffic budgets affecting a query fingerprint
pscale insights queries show <database> <branch> <query-id> --org <org> --format json     # one execution; query-id comes from the samples list
pscale insights queries summary <database> <branch> <fingerprint> --org <org> --format json --[redacted]space <[redacted]space>  # aggregate stats for one query pattern
pscale insights errors <database> <branch> --org <org> --format json                     # failing queries with error messages
pscale insights errors show <database> <branch> <fingerprint> --org <org> --format json  # individual queries behind one error fingerprint (use error_fingerprint from the errors list)
pscale insights anomalies <database> <branch> --org <org> --format json                  # detected resource anomalies (CPU, memory, IOPS, rows)
pscale insights anomalies show <database> <branch> <id> --org <org> --format json        # one anomaly plus its correlated queries
pscale insights tags <database> <branch> --org <org> --format json                       # query tag [redacted]s (sqlcommenter / system); use names with summaries
pscale insights tags summaries <database> <branch> --org <org> --format json --tags username  # stats grouped by tag; names match the Insights UI [redacted] picker
pscale insights recommendations <database> --org <org> --format json                     # schema recommendations with ready-to-apply DDL
pscale insights recommendations show <database> <number> --org <org> --format json        # one recommendation plus full DDL (number from list)
pscale insights recommendations dismiss <database> <number> --org <org> --format json --force  # dismiss a recommendation
pscale branch query-patterns list <database> <branch> --org <org> --format json
pscale branch query-patterns show <database> <branch> <report-id> --org <org> --format json
pscale branch query-patterns delete <database> <branch> <report-id> --org <org> --format json --force
```

`queries show` takes an individual execution/sample `id`; `queries samples` and `queries summary` take a query `fingerprint`. These identifiers are not interchangeable. `queries summary` requires the [redacted]space from the queries list and accepts `--period`, or a paired `--from`/`--to` ISO 8601 range.

`branch query-patterns download` generates a new report, waits, and writes CSV. Use list/show/delete for reports that already exist.

**`pscale inspect`** — live, point-in-time checks run over a direct connection (same credentials model as `pscale sql`, always read-only):

```bash
pscale inspect all <database> <branch> --org <org> --format json    # every applicable check, one report
pscale inspect <check> <database> <branch> --org <org> --format json
```

Checks: `table-sizes`, `index-sizes`, `unused-indexes`, `redundant-indexes`, `seq-scans`, `long-running-queries`, `locks`, `outliers`, `calls`, `bloat`, `vacuum-stats`, `replication-slots`, `subscriptions`. Checks adapt per engine; ones that don't apply explain the alternative. JSON results include `next_steps` pointing at the matching `insights` command — follow them.

Caveats:
- Statistics are since last server restart and per-connection-target: on sharded Vitess databases they reflect a single shard's MySQL instance. Use `--[redacted]space` to pick a [redacted]space, or pin an exact shard with `--[redacted]space 'my[redacted]space/-80'` (enumerate with `pscale sql <database> <branch> --org <org> --format json --query "SHOW VITESS_SHARDS"`; rows are `[redacted]space/shard`). Databases can have hundreds of shards — inspect one shard at a time rather than fanning out. On PostgreSQL, stats are scoped to one database (use `--dbname`; if CONNECT is denied, retry with `--role admin`).
- `outliers`/`calls` need `pg_stat_statements` on PostgreSQL; if missing, use `pscale insights queries` instead (no extension needed).
- Rule of thumb: start with `insights` (traffic-aware, historical), use `inspect` for live state (locks, in-flight queries) and physical layout (sizes, bloat, index usage).

## Vitess database throttler

Database-level default for future deploy request migrations (not per-DR, not tablet/vtctld):

```bash
pscale database throttler show <database> --org <org> --format json
pscale database throttler update <database> --org <org> --format json --ratio 25
pscale database throttler update <database> --org <org> --format json \
  --configuration main=10 --configuration sharded=40
```

`--ratio` is 0–95 (0 disables throttling; 95 is slowest). Use either `--ratio` or `--configuration [redacted]space=ratio`, not both. Vitess only.

## Vitess aggressive cutover

Database-level setting for future deploy requests (not the same as `deploy-request force-cutover`):

```bash
pscale database aggressive-cutover show <database> --org <org> --format json
pscale database aggressive-cutover enable <database> --org <org> --format json
pscale database aggressive-cutover disable <database> --org <org> --format json
```

Vitess only. See https://planetscale.com/docs/vitess/schema-changes/aggressive-cutover

## Vitess deploy requests (inspect + throttler)

Core lifecycle is already covered (`list/create/show/diff/review/deploy/apply/unblock/update/cancel/close/revert/skip-revert`). `update` (`edit` is an alias) sets auto-apply and auto-delete-branch. `unblock` clears the queue after a failed deploy or revert (dashboard “Unblock deploy queue”); it is not `apply`. These inspect commands are read-only:

```bash
pscale deploy-request queue <database> --org <org> --format json                         # database deploy queue (first page)
pscale deploy-request operations <database> <number> --org <org> --format json           # per-table schema ops + progress
pscale deploy-request reviews <database> <number> --org <org> --format json              # existing reviews (create with review)
pscale deploy-request deployment <database> <number> --org <org> --format json           # deployment detail (cutover flags, queue state)
pscale deploy-request storage-check <database> <number> --org <org> --format json        # enough_storage / bytes needed
pscale deploy-request throttler show <database> <number> --org <org> --format json       # per-DR throttler ratios (not database throttler)
```

Throttler update mutates the deploy request (use after `throttler show`):

```bash
pscale deploy-request throttler update <database> <number> --org <org> --format json --ratio 25
pscale deploy-request throttler update <database> <number> --org <org> --format json \
  --configuration main=10 --configuration sharded=40
```

Alias: `pscale dr …` works the same. Vitess only. `--ratio` is 0–95 (0 disables throttling; 95 is slowest). Use either `--ratio` or `--configuration [redacted]space=ratio`, not both.

```bash
pscale deploy-request update <database> <number> --org <org> --format json --enable-auto-apply
pscale deploy-request update <database> <number> --org <org> --format json --auto-delete-branch=false
```

After a failed deploy or revert (`complete_error` / `complete_revert_error`), unblock the queue. This is not `apply` (gated cutover) and it cannot fix a deploy-check `error`:

```bash
pscale deploy-request unblock <database> <number> --org <org> --format json
```

## Maintenance schedules (Vitess Enterprise)

Read-only visibility into planned maintenance windows for a Vitess database (Enterprise plans):

```bash
pscale maintenance list <database> --org <org> --format json
pscale maintenance show <database> <schedule-id> --org <org> --format json
pscale maintenance windows <database> <schedule-id> --org <org> --format json
```

`--org` is required. Schedules include next/last window times, frequency, and any pending Vitess/MySQL version updates. Vitess Enterprise only.

## Postgres branch changes (size, replicas, parameters) — Postgres only

`pscale branch resize` queues a single asynchronous **change request** for a Postgres branch covering cluster size, replica count, and configuration parameters in any combination. Track it with `resize status`; cancel it with `resize cancel` while queued.

```bash
# Read the parameter catalog first (names, current/default values, restart/immutable flags)
pscale branch parameters list <database> <branch> --org <org> --format json
pscale branch parameters list <database> <branch> --org <org> --format json --namespace pgconf

# Extensions available on the cluster image (not CREATE EXTENSION state)
pscale branch extensions list <database> <branch> --org <org> --format json

# Default postgres role (read-only; reset-default rotates the password)
pscale role default <database> <branch> --org <org> --format json
pscale role reset-default <database> <branch> --org <org> --format json --force

# Role connection details for a branch replica, read-only replica, or PgBouncer
pscale role get <database> <branch> <role-id> --org <org> --format json --replica
pscale role get <database> <branch> <role-id> --org <org> --format json --read-only-replica <replica-name>
pscale role get <database> <branch> <role-id> --org <org> --format json --bouncer <bouncer-name>

# Change parameters (repeat --parameters; [redacted]s are namespace.name)
pscale branch resize <database> <branch> --org <org> --format json --parameters pgconf.max_connections=200

# Combine size, replicas, and parameters into one change request
pscale branch resize <database> <branch> --org <org> --format json \
  --cluster-size PS_10_GCP_X86 --replicas 2 --parameters pgconf.max_connections=500

# Block until the change finishes (default timeout 10m; tune with --wait-timeout)
pscale branch resize <database> <branch> --org <org> --format json --parameters pgconf.work_mem=64MB --wait

# Inspect the latest change request / cancel a queued one
pscale branch resize status <database> <branch> --org <org> --format json
pscale branch resize cancel <database> <branch> --org <org> --format json
```

- At least one of `--cluster-size`, `--replicas`, or `--parameters` is required.
- The `role get` connection target flags are mutually exclusive. Targeted role responses keep the same shape while changing `username`, `access_host_url`, and `database_url` as needed.
- `--parameters` values are validated against the catalog before submission; unknown or immutable parameters fail fast. Parameters with `"restart": true` in the catalog restart the database when applied — surface this to the user before changing them.
- Change request `state` is one of `queued`, `pending`, `resizing`, `completed`, `canceled`. Only `completed` and `canceled` are terminal. Without `--wait`, poll `resize status` instead of assuming completion.
- A no-op (branch already matches the requested configuration) prints `{"result": "no_change", "branch": "<branch>"}` in JSON mode instead of a change request.
- `resize cancel` prints `{"result": "canceled", "branch": "<branch>"}` in JSON mode.
- MySQL databases are rejected: use `pscale [redacted]space resize` for Vitess [redacted]spaces.

## Postgres read-only replicas

Read-only replicas provide dedicated regional capacity for Postgres queries
that can tolerate replication lag. They are separate from the replicas in the
primary branch cluster and from Vitess read-only regions.

```bash
pscale read-only-replica list <database> <branch> --org <org> --format json
pscale read-only-replica show <database> <branch> <name> --org <org> --format json
pscale read-only-replica create <database> <branch> <name> --region <region> --org <org> --format json
pscale read-only-replica create <database> <branch> <name> --region <region> --replicas 2 --cluster-size PS_10_GCP_X86 --org <org> --format json
pscale read-only-replica update <database> <branch> <name> --replicas 3 --org <org> --format json
pscale read-only-replica update <database> <branch> <name> --cluster-size PS_20_GCP_X86 --parameters pgconf.max_connections=300 --org <org> --format json
pscale read-only-replica delete <database> <branch> <name> --org <org> --format json --force
```

- `create` requires a name and `--region`. The API defaults to one instance and the primary cluster size when `--replicas` and `--cluster-size` are omitted.
- `show`, `update`, and `delete` identify the read-only replica by name.
- `update` requires at least one of `--replicas`, `--cluster-size`, or repeatable `--parameters namespace.name=value`. Parameter values must be greater than or equal to the primary's corresponding values.
- Creating and updating replicas is asynchronous; inspect `state` and `ready` in the returned object or with `list`.
- `delete` requires explicit approval before using `--force`.
- PostgreSQL only. For Vitess/MySQL, use `pscale [redacted]space read-only-regions`.

## Postgres switchovers

`pscale branch switchover` moves the primary of a Postgres branch to a replica. It is Postgres-only; Vitess/MySQL databases are rejected before any API call.

```bash
# Promote an automatically selected replica
pscale branch switchover <database> <branch> --org <org> --format json

# Promote a specific replica (names from `pscale branch infra`)
pscale branch switchover <database> <branch> --org <org> --format json --candidate <replica-name>

# List switchovers for a branch (supports --page and --per-page)
pscale branch switchover list <database> <branch> --org <org> --format json

# Show current status and details for one switchover
pscale branch switchover show <database> <branch> <id> --org <org> --format json
```

- The command returns the created switchover (`id`, `state`, `method`) and exits; it does not wait. A fresh switchover is `pending` and `method` is empty until the operator picks one.
- `method` is `switchover` (replica promoted) for branches with replicas, or `restart` for single-node branches, which are restarted in place and unreachable while they come back. Warn the user before running this against a single-node branch.
- Writes are briefly interrupted while the switch completes. A branch accepts one switchover at a time.
- A switchover that ends in `failed` has an unconfirmed outcome: the primary may still have moved and nothing is rolled back. Check the current primary with `pscale branch infra <database> <branch> --org <org> --format json` before retrying.
- `--candidate` is rejected for branches without replicas. Poll status with `pscale branch switchover show <database> <branch> <id> --org <org> --format json`.

## Postgres branch maintenance

`pscale branch maintenance run` upgrades a Postgres branch to the latest cluster image. This is how regular version bumps, bugfixes, and quality-of-life improvements reach a branch; PlanetScale otherwise upgrades images only in emergencies, such as patching security issues.

```bash
# Run maintenance now
pscale branch maintenance run <database> <branch> --org <org> --format json

# Also upgrade to the latest PostgreSQL minor version
pscale branch maintenance run <database> <branch> --org <org> --format json --update-postgres-minor-version
```

- The upgrade is applied to the replicas first, followed by a switchover from the old primary to an upgraded replica. That failover leads to a short period of database unavailability (seconds) and terminates all direct connections. A branch running a single instance has no replica to switch over to and is unavailable until it comes back. Warn the user before running this.
- The command returns `{"result": "maintenance started", "branch": "<branch>"}` and exits; it does not wait. Check progress with `pscale branch infra <database> <branch> --org <org> --format json`.
- Rejected while a change request from `pscale branch resize` is still in progress — check `pscale branch resize status` first.
- `--update-postgres-minor-version` is rejected when the branch is already on the latest minor version or the upgrade is unavailable for it.
- Postgres only; Vitess/MySQL databases are rejected before any API call.
- See https://planetscale.com/docs/postgres/operations-philosophy

## Billing payment methods

Update the organization's card through Stripe-hosted Checkout. The CLI never collects PAN; the human must finish Checkout in a browser. There is only one current card.

```bash
pscale billing payment-method update --org <org> --format json
pscale billing payment-method status <setup-id> --org <org> --format json
pscale billing payment-method show --org <org> --format json
pscale billing payment-method delete --org <org> --format json --force
```

`--org` is required (same as other resource commands). `status <setup-id>` takes the `id` returned by `update` (pending JSON on stderr, or the setup object on stdout). That id is a **setup** id, not the saved card id. `show` and `delete` operate on the organization's current card.

**Happy path — leave `update` running.** It creates a setup, opens Checkout when possible, and **polls until a terminal state**. Do not poll `status` yourself unless `update` was interrupted.

Same JSON shape as `auth login`: pending object on **stderr** immediately; a single setup object on **stdout** when polling finishes.

```json
{
  "status": "pending",
  "id": "<setup-id>",
  "checkout_url": "https://checkout.stripe.com/...",
  "browser_opened": true,
  "message": "Complete Stripe Checkout in the browser to continue",
  "next_steps": [
    "Complete Stripe Checkout",
    "pscale billing payment-method status <setup-id> --org <org> --format json"
  ]
}
```

1. Surface `checkout_url` to the user. If `browser_opened` is false, tell them to open it. You cannot complete Checkout.
2. Keep `update` running. Do not start a second `update` while the first is waiting — that creates a new Checkout session.
3. When `update` exits, read **stdout**. Branch on `state`: `completed`, `failed`, or `expired`. `failed` includes a user-facing `error`.

A non-`completed` terminal state exits non-zero and prints an error envelope on **stdout** carrying `id`, `state`, and the recovery command:

| Code | Meaning |
|------|---------|
| `PAYMENT_METHOD_SETUP_FAILED` | Verification failed; `error` has the user-facing reason. The Checkout session is spent — run `update` again. |
| `PAYMENT_METHOD_SETUP_EXPIRED` | Checkout was not completed in time; run `update` again. |
| `PAYMENT_METHOD_SETUP_INTERRUPTED` | Polling stopped while the setup was still pending. `action_required`; resume with `status <setup-id>`, do not run `update` again. |

Follow `next_steps`. Only `PAYMENT_METHOD_SETUP_INTERRUPTED` is resumable; for the other two, `status` will keep returning the same terminal state.

**If `update` is killed or times out**, take `id` from the pending stderr object (do not call `update` again) and GET once:

```bash
pscale billing payment-method status <setup-id> --org <org> --format json
```

`status` does **not** poll. If `state` is still `pending`, wait and call `status` again with the same setup id. Same terminal states as `update`.

**Current card.** `show` returns the saved card. `delete` requires user approval, then `--force` in JSON (`CONFIRMATION_REQUIRED` without it). Failures print the same envelope as other agent commands (`status`, `error`, `issues`, `next_steps`):

| Code | Meaning |
|------|---------|
| `NOT_FOUND` | No current card. Next step is `pscale billing payment-method update --org <org> --format json`. |
| `UNPAID_INVOICES` | `delete` rejected because the organization has unpaid invoices. Pay them, then retry. |

## Billing invoices

Read-only invoice history for an organization. `--org` is required.

```bash
pscale billing invoice list --org <org> --format json
pscale billing invoice show <invoice-id> --org <org> --format json
pscale billing invoice line-items <invoice-id> --org <org> --format json
```

- `list` and `line-items` are paginated and return **one page per call**: `--page` (default 1) and `--per-page` (default 25). Invoices with thousands of line items are common, so walk pages deliberately. Human output prints the next page number; in JSON compare the row count to `--per-page` to decide whether to fetch again.
- JSON preserves the API objects. Line items include the billed database and nested resource (usually a branch).
- Requires `read_invoices` on a service [redacted].

## Imports (Cloudflare D1) — Postgres only

`pscale import d1` migrates a Cloudflare D1 (SQLite) export into a PlanetScale Postgres branch. Every subcommand supports `--format json` and returns `status`, `issues`, and `next_steps`; stateful steps return a `migration_id` — pass it back with `--migration-id` to resume.

```bash
pscale import d1 doctor --format json                              # check prerequisites (pgloader, psql)
pscale import d1 lint --input <file> --format json                 # pre-import checks; errors block import
pscale import d1 start <database> --org <org> --input <file> --dry-run --format json  # plan + migration ID, no writes
pscale import d1 start <database> --org <org> --input <file> --format json            # run the import
pscale import d1 verify <database> --org <org> --migration-id <id> --sqlite <file> --format json
pscale import d1 complete <database> --org <org> --migration-id <id> --format json
```

Branch is an optional second positional (defaults to the default branch). `status --migration-id <id>` shows saved migration state; `convert-schema --input <file>` converts schema only.

**`start` in JSON mode does not prompt.** The confirmation prompt is human-format only; with `--format json`, `start` loads data immediately. Run `start --dry-run` first, show the user the plan, and only run the real `start` after they approve.

## API passthrough

```bash
pscale api --org <org> organizations/<org>/databases
```

## MCP

For MCP clients, use the hosted PlanetScale MCP server:

```text
https://mcp.pscale.dev/mcp/planetscale
```

See the current MCP docs: https://planetscale.com/docs/connect/mcp

## PlanetScale agent skills

Operational workflows (inventory, safety review, Insights, schema recommendations, Traffic Control) live in the public skills repo — not in this file.

```bash
git clone https://github.com/planetscale/skills.git && cd skills && script/setup
# or: npx skills add planetscale/skills -g -y
```

After installing skills, load `14-pscale-cli-automation` for CLI conventions (or run `pscale --skill` from any `pscale` binary to print this reference). Use `00-safe-orchestrator` when the user asks for a full PlanetScale assessment.
$pscale --version # installed via `brew install planetscale/tap/pscale`reproduced
$ pscale --version  # installed via `brew install planetscale/tap/pscale`
pscale version 0.329.0 (build date: 2026-09-04T21:14:34Z commit: 1db86769)
proves: Use an official CLIrecorded 2026-09-06
$curl -si -X POST https://mcp.pscale.dev/mcp/planetscale -H 'Content-Type: application/json' -d '<jsonrpc initialize>'reproduced
$ curl -si -X POST https://mcp.pscale.dev/mcp/planetscale -H 'Content-Type: application/json' -d '<jsonrpc initialize>'
HTTP/2 401

date: Sun, 06 Sep 2026 21:10:37 GMT

content-type: application/json

content-length: 81

access-control-allow-credentials: true

access-control-allow-headers: Accept, Content-Type, Content-Length, Accept-Encoding, Authorization, User-Agent, Gram-Session, Gram-Project, Gram-[redacted], Gram-[redacted], idempotency-[redacted], Gram-Admin-Override, Gram-Chat-ID, Gram-Assistant-ID, Gram-Chat-Session, MCP-Protocol-Version, Mcp-Method, Mcp-Name, Mcp-Session-Id, X-Gram-Scope-Override, X-Gram-Source

access-control-allow-methods: POST, GET, OPTIONS, PUT, DELETE

access-control-allow-origin: https://app.getgram.ai

access-control-expose-headers: Accept, Content-Type, Content-Length, Accept-Encoding, x-trace-id, Gram-Session, Gram-Chat-ID, Gram-Chat-Session, Mcp-Session-Id

mcp-session-id: c8358496-bddd-446d-a76c-97ac6dde8118

www-authenticate: Bearer resource_metadata="https://mcp.pscale.dev/.well-known/oauth-protected-resource/mcp/planetscale"

x-request-id: 11972c46e90f47780459733ad70e3025

x-trace-id: 1cfe1aadc716291255789b81d6485e0d

strict-transport-security: max-age=31536000; includeSubDomains

content-security-policy: base-uri 'none'; connect-src 'self' https://api.github.com https://metrics.speakeasy.com https://browser-intake-datadoghq.com https://*.usepylon.com wss://*.pusher.com https://*.pusher.com https://*.posthog.com https://chat.speakeasy.com https://chat.dev.speakeasy.com https://app.getgram.ai https://dev.getgram.ai https://cdn.prod.getgram.ai https://cdn.dev.getgram.ai https://app.cal.com https://storage.googleapis.com https://*.clairedefermat.com; default-src 'self'; font-src 'self' https://*.getgram.ai https://fonts.googleapis.com https://fonts.gstatic.com https://*.usepylon.com https://*.posthog.com; frame-ancestors 'self'; frame-src 'self' https://polar.sh https://*.polar.sh https://app.svix.com https://app.cal.com https://cal.com; img-src 'self' blob: https: android-webview-video-poster: data:; media-src 'self' https://*.posthog.com; object-src 'none'; script-src 'self' 'wasm-unsafe-eval' https://www.datadoghq-browser-agent.com https://metrics.speakeasy.com https://widget.usepylon.com https://*.posthog.com https://*.getgram.ai https://app.cal.com https://*.clairedefermat.com; style-src 'self' 'unsafe-inline' https://*.getgram.ai https://fonts.googleapis.com https://*.usepylon.com https://*.posthog.com; worker-src 'self' blob:; report-to browser-intake-datadoghq

permissions-policy: fullscreen=(), compute-pressure=(), camera=(), microphone=(self), geolocation=(), accelerometer=(), bluetooth=(), gyroscope=(), payment=(), usb=(), midi=(), magnetometer=()

referrer-policy: strict-origin-when-cross-origin

reporting-endpoints: browser-intake-datadoghq="https://browser-intake-datadoghq.com/api/v2/logs?dd-[redacted]

x-content-type-options: nosniff

x-frame-options: deny

x-xss-protection: 1; mode=block

{"error":{"code":-32001,"message":"unauthorized access"},"id":1,"jsonrpc":"2.0"}

Claims vs evidence — vendor claims reconciled against independent verdictsClaims vs evidence

12 of 19 testable claims verified · 0 contradictedintegrity 63/100

29 distinct capability claims found in PlanetScale’s own claimed-docs/GitHub materials, reconciled against our judge’s independent verdicts.

12

Verified

7

Unverified

0

Contradicted

18

Undersold

Verified (19)
Unverified (12)
Undersold (18)
Claims outside our story set (4)

Real capability claims found in PlanetScale’s own materials, but no story in this arena’s taxonomy covers them yet — that’s feedback on the taxonomy, not a mark against the product.

  • Large monolithic databases can be sharded and spread across multiple servers

    source ↗
  • Metal offering provides high-IOPS NVMe SSD performance for demanding workloads

    source ↗
  • Metal delivers reduced latency, consistent IO, and unlimited IOPS

    source ↗
  • Sharding logic can be kept out of application code

    source ↗
Suggest a story for these →

Pricing signals

  • $5per month (entry plan)entry planPlanetScale Postgres single node starting price for development/low-traffic workloadssource ↗as of 2026-09-07
  • freeper GB-monthfree tierFirst 10 GB of network-attached storage is included free before per-GB pricing appliessource ↗as of 2026-09-07

Extracted verbatim from the vendor’s own pricing page — hover a figure for the exact quote.

Business model

usage-basedsubscription-flatenterprise-custom

Per-cluster pricing from $15/month (PS-5) by instance size, storage, and egress across Postgres and Vitess/MySQL; Metal NVMe clusters and Managed/BYOC enterprise deployments are priced custom. No free tier.

pricing ↗

Score trend

How this product’s scores have moved as evidence and verdicts are re-derived — a point per change, not per day.

PA Scoretracked since Sep 6 '26 — no movement recorded yet
Agent-readytracked since Sep 6 '26 — no movement recorded yet

Try Experimental

Run it in the microterminal →

Recorded agent sessions — and a live MCP handshake where the vendor ships one.

Flag

⚑ Flag a verdict

Think a verdict is wrong? Opens a prefilled GitHub issue — or use the ⚑ next to any verdict above.

Badge

Embed this product's score badge →

Hotlinked SVG — always shows the live current score.

For agents

Data

⚿ auth1 auth-gated probe

Agent surface uptime MCP 100% · llms.txt 100% (30d, checked every 6h since Sep 8 '26)