> ## Documentation Index
> Fetch the complete documentation index at: https://spreecommerce.org/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Call the Admin API from the CLI

> Use spree api for generic get/post/patch/delete calls, schema introspection, and zero-config credentials — built for scripts and AI agents.

The `spree api` command group turns the CLI into a first-class Admin API client: generic HTTP verbs against any endpoint, schema introspection without leaving the terminal, and credentials that resolve automatically — from a local dev project to a production store profile. It works against any Spree 5.5+ instance and is designed to be driven by both humans and AI coding agents.

```bash theme={"theme":"night-owl"}
spree api get /products -q status_eq=active --sort -created_at --limit 10
spree api get /orders/ord_x8k2J9aQ --expand items,payments
spree api post /products -d '{"name":"Classic Tee","prices":[{"currency":"USD","amount":"29.99"}]}'
spree api patch /orders/ord_x8k2J9aQ/cancel
spree api endpoints --resource orders
spree api schema "POST /orders/{id}/refunds"
spree api status
```

## Credentials

Credentials resolve through four layers — first match wins:

| Layer       | How                                                                                                                                                                                                          |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Flags       | `--base-url` / `--api-key` (and `--profile`, which selects a saved profile explicitly)                                                                                                                       |
| Environment | `SPREE_API_KEY` (+ `SPREE_BASE_URL`) — the path for CI and agents. `SPREE_BASE_URL` is optional: a bare key defaults to `http://localhost:3000`, so local dev needs only the key. Set it for a remote store. |
| Project     | Inside a Spree project with the dev stack running, the CLI mints a **read-only** key on first use and stores it in `.spree/credentials.json` (gitignored)                                                    |
| Profile     | `~/.config/spree/config.json`, written by `spree auth login`                                                                                                                                                 |

Host and key resolve **together** per source — a saved or minted key is never silently re-pointed at a host from a different layer. Only `--base-url` (or `SPREE_BASE_URL` paired with `SPREE_API_KEY`) re-points a key deliberately. So against local dev:

```bash theme={"theme":"night-owl"}
SPREE_API_KEY=sk_xxx spree api get /products    # → http://localhost:3000
```

Zero configuration is needed for local development — the first `spree api get` inside your project just works:

```bash theme={"theme":"night-owl"}
cd my-store
spree api get /products
# No credentials found — minting a read-only API key via the dev stack...
# Saved to .spree/credentials.json (scopes: read_all)
```

Auto-minted keys carry `read_all` only. Writes always require an explicitly created key:

```bash theme={"theme":"night-owl"}
spree api-key create --type secret --scopes read_orders,write_products
```

For remote stores, create a secret key in the admin under **Settings → API Keys** and save it as a profile — the key is read from a prompt, never from a flag:

```bash theme={"theme":"night-owl"}
spree auth login --profile prod --base-url https://store.example.com
spree api get /orders --profile prod -q "status_eq=complete"
spree auth status
spree auth logout --profile prod
```

## Reading data

`-q` takes [Ransack](/api-reference/admin-api/querying) predicates as repeatable `key=value` pairs; sorting, pagination, and shaping flags pass through under the Admin API conventions:

```bash theme={"theme":"night-owl"}
spree api get /products -q name_cont=shirt -q status_eq=active
spree api get /orders --sort -created_at --page 2 --limit 50
spree api get /products/prod_86Rf07xd --expand variants,variants.prices
spree api get /customers --fields id,email --format table
```

`--fields` trims the response to the attributes you name (comma-separated). The resource `id` is **always returned** even if you don't list it, so you only need to name the extra fields. `--expand` is the companion axis — `--fields` selects top-level attributes, `--expand` pulls in related resources:

```bash theme={"theme":"night-owl"}
spree api get /products --fields name,price          # → id + name + price
spree api get /products --fields name --expand variants
```

### Output format

Output is JSON on stdout, and it adapts to where it goes:

* **In a terminal** — indented and syntax-colored, for reading.
* **Piped or redirected** — compact, single-line, uncolored, so it stays fast and feeds cleanly into `jq` (color codes would corrupt it).

```bash theme={"theme":"night-owl"}
spree api get /products --limit 5 | jq '.data[].name'
```

`--format table` renders collections as a table for humans (nested objects collapse to `{…}`, arrays to `[N]`).

## Writing data

Request bodies are flat JSON (no root wrapping), inline, from a file, or from stdin:

```bash theme={"theme":"night-owl"}
spree api post /products -d '{"name":"Classic Tee","prices":[{"currency":"USD","amount":"29.99"}]}'
spree api post /orders/ord_x8k2J9aQ/refunds -d @refund.json
cat prices.json | spree api post /prices/bulk_upsert -d -
spree api delete /products/prod_86Rf07xd
```

Mutations automatically carry an `Idempotency-Key`, so retries are safe.

### Creating products

A product is a catalog grouping; everything purchasable — SKU, price, stock, weight — lives on its variants. For a **simple product** with no options, ship a top-level `prices` array and Spree creates the single backing variant for you:

```bash theme={"theme":"night-owl"}
spree api post /products -d '{"name":"Classic Tee","prices":[{"currency":"USD","amount":"29.99"}]}'
```

For a product with options, send the `variants` array, each with its own `options` and `prices` (and optionally `stock_items`):

```bash theme={"theme":"night-owl"}
spree api post /products -d '{
  "name": "Classic Tee",
  "variants": [
    { "sku": "TEE-S", "options": [{"name":"size","value":"Small"}], "prices": [{"currency":"USD","amount":"29.99"}] },
    { "sku": "TEE-M", "options": [{"name":"size","value":"Medium"}], "prices": [{"currency":"USD","amount":"29.99"}] }
  ]
}'
```

Run `spree api schema "POST /products"` for the full request body.

## Discovering endpoints and schemas

The CLI bundles a snapshot of the Admin API OpenAPI spec, so discovery works offline:

```bash theme={"theme":"night-owl"}
spree api endpoints --resource orders          # methods, paths, required scopes
spree api endpoints --search "gift card"
spree api schema "POST /orders"                # parameters + request/response schema
spree api status                               # resolved credentials + server reachability
```

`spree api endpoints` shows the `read_*` / `write_*` scope each endpoint requires (see [Authentication](/api-reference/admin-api/authentication)) — useful for deciding what to grant a new key.

### Shell completion

Tab-completion suggests resource paths, Ransack predicate stems (`name_cont=`, `status_eq=`, …), and scope names — all offline from the bundled spec. Add the generated script to your shell:

```bash theme={"theme":"night-owl"}
# zsh — add to ~/.zshrc
eval "$(spree completion zsh)"
# bash — add to ~/.bashrc
eval "$(spree completion bash)"
# fish — add to ~/.config/fish/config.fish
spree completion fish | source
```

Then `spree api get /pro⇥` completes to `/products`, and `spree api get /products -q name_⇥` offers the Ransack predicates.

## Errors and exit codes

API errors print the error envelope to stderr and exit `1`; usage and configuration problems exit `2`. A scope denial includes the exact remediation:

```text theme={"theme":"night-owl"}
access_denied: API key lacks scope: write_products
{
  "details": { "required_scope": "write_products" }
}
Hint: mint a key carrying this scope — spree api-key create --type secret --scopes write_products
```

## Using it with AI agents

Agents with shell access (Claude Code, Cursor, and similar) can drive a Spree back office through `spree api` with no extra configuration: inside a project the credentials self-provision read-only, and for remote stores two environment variables (`SPREE_BASE_URL`, `SPREE_API_KEY`) are the whole contract. The `endpoints`/`schema` commands give agents request shapes on demand instead of loading the entire API surface up front, and error messages name the missing scope so agents can self-correct.
