Klaviyo CLI
Before you begin
The Klaviyo CLI lets you call every Klaviyo API operation as a typed command from your terminal — build, test, and script against your Klaviyo integration without leaving the command line, in the spirit of the Stripe CLI.
You can also find installation instructions and the full command reference in the Klaviyo CLI README.
This is a new CLI and replaces the previous Headless Klaviyo CLI (
headless-klaviyo), which managed campaigns, flows, segments, and universal content blocks as local JSON files. That file-based workflow has been retired — see Coming from the Headless Klaviyo CLI below if you have scripts built against it.
Install Klaviyo CLI
Binary releases (amd64 and arm64) are on the Releases page. These one-liners stream the release straight to your install directory with the GitHub CLI — they leave nothing behind in the working directory and are safe to re-run for upgrades.
macOS
gh release download -R klaviyo/klaviyo-cli --pattern '*darwin_arm64.tar.gz' -O - | sudo tar -xz -C /usr/local/bin klaviyo
# Intel Macs: swap in darwin_amd64
Linux
gh release download -R klaviyo/klaviyo-cli --pattern '*linux_amd64.tar.gz' -O - | sudo tar -xz -C /usr/local/bin klaviyo
# ARM: swap in linux_arm64
-C can point at any directory on your PATH; drop sudo for one you own.
Windows (PowerShell)
gh release download -R klaviyo/klaviyo-cli --pattern '*windows_amd64.zip' -O klaviyo-cli.zip --clobber # ARM: windows_arm64
Expand-Archive klaviyo-cli.zip -DestinationPath klaviyo-cli-release -Force
Move-Item -Force klaviyo-cli-release\klaviyo.exe "$env:LOCALAPPDATA\Microsoft\WindowsApps\" # or any directory on your PATH
Downloading an archive from the Releases page by hand works too — unpack it and put the klaviyo binary on your PATH. The binaries aren't notarized yet, so macOS quarantines browser-downloaded files (gh downloads are not quarantined) — clear it with sudo xattr -d com.apple.quarantine /usr/local/bin/klaviyo.
From source (requires Go 1.25+):
go install github.com/klaviyo/klaviyo-cli/cmd/klaviyo@latest
Verify the install:
klaviyo version
Update Klaviyo CLI
To upgrade, re-run the install command for your platform — it fetches the latest release and overwrites the installed binary in place (same for go install ...@latest). Upgrade through the channel you installed with.
The CLI also checks GitHub for a newer release at most once a day and prints a notice to stderr — only on an interactive terminal, never in CI or when piped. It never self-updates. Disable the check with KLAVIYO_NO_UPDATE_NOTIFIER=1 (also disabled automatically whenever CI is set).
Authenticate
A private API key with the read/write scopes you need is required. See how to create a private API key.
klaviyo auth login
klaviyo auth status
auth login verifies the key against the API, then stores it under a named account profile — the name defaults to the key's organization name, and the first account added becomes the default. Keys live in your OS keychain (macOS Keychain, Windows Credential Manager, Linux Secret Service), not in a plaintext file. Where no keychain is available — headless Linux, containers — pass --insecure-storage to store the key in ~/.config/klaviyo/config.toml instead (written with 0600 permissions).
Store as many accounts as you like and switch between them:
klaviyo auth login --account prod
klaviyo auth login --account staging
klaviyo auth list # * marks the default
klaviyo auth switch staging # change the default (by name or account ID)
klaviyo api /api/metrics/ --account prod # one-off override for a single command
For scripts and agents, skip the interactive prompt and pipe the key in instead, keeping it out of shell history:
printf '%s' "$KLAVIYO_KEY" | klaviyo auth login --api-key-stdin
The API key used for a given request resolves in this order:
--api-keyflagKLAVIYO_API_KEYenvironment variable- The selected account's stored key — chosen by
--accountflag, thenKLAVIYO_ACCOUNTenvironment variable, then the configured default account
In CI or other headless environments, skip stored accounts entirely and set KLAVIYO_API_KEY.
CLI commands
The CLI is invoked as klaviyo <command> or, for API resources, klaviyo <group> <command>.
Core commands
| Command | Description |
|---|---|
klaviyo auth login | Store an API key for a named account (verified first) |
klaviyo auth logout <account> | Remove an account and its key |
klaviyo auth list | List configured accounts |
klaviyo auth switch <account> | Set the default account |
klaviyo auth status | Verify credentials for the selected account |
klaviyo api [method] <path> | Raw authenticated API request (defaults to GET) |
klaviyo config | Show or edit CLI configuration (--list, --set, -e) |
klaviyo open <shortcut> | Open a Klaviyo dashboard or docs page in your browser |
klaviyo completion <shell> | Generate shell completion scripts |
klaviyo version | Print the CLI version |
Resource commands
Every JSON operation in the Klaviyo API has a matching command, generated directly from the OpenAPI spec — grouped by resource, one command per operation. (A couple of operations that take a non-JSON body — file uploads, mainly — aren't generated as typed commands; reach those with the raw klaviyo api escape hatch below.) Run klaviyo --help to list every group, or klaviyo <group> --help for that group's commands:
klaviyo profiles --help
klaviyo profiles list
klaviyo profiles get 01H260JDT1NJVY1EF61ET64Z7F --fields-profile email,first_name
klaviyo profiles create --email [email protected] --location.city Boston
klaviyo lists list --paginate # follow cursors, merge all pages
klaviyo lists create --name Newsletter
klaviyo events create -d @event.json # or supply the whole body yourself
klaviyo templates create-universal-content --name "Test UC Block"
A few conventions hold across every resource group:
- Canonical CRUD is
list,get,create,update,delete. Every other operation keeps a descriptive name derived from the API operation itself (klaviyo profiles get-lists-for-profile). - Path parameters are positional arguments; query parameters are flags (
page[size]becomes--page-size). - Body attributes are flags, too. Each scalar field under the request body's
data.attributesgets its own typed, documented flag — dots for nested objects (--location.city Boston), repeats for arrays.--helpon any command lists every field with its description straight from the API spec. - Anything a flag can't express — free-form maps like event
properties, arrays of objects, relationships, bulk (array-data) endpoints — uses-d/--data: repeatablepath=valuepairs (dots nest objects,:=assigns a JSON value), or a single-dwith inline JSON,@file, or-for stdin. Flags and-dpairs merge into one request body; conflicting fields are an error. - List commands accept
--paginateto followlinks.nextcursors and merge every page'sdataarray into one response.
Resource names follow the current API — for example, universal content blocks are templates create-universal-content / templates update-universal-content / templates get-universal-content, matching the Universal Content API rather than the older "block" terminology.
Beta API operations
Endpoints still in beta — including newer resources like custom objects, translations, and the Customer Agent API — live under a separate klaviyo beta <group> <command> prefix, generated the same way as stable commands. Every call made through klaviyo beta sends the beta revision header automatically, so you don't need --revision to reach them:
klaviyo beta custom-objects get-object-types
klaviyo beta translations list
Scripting and automation
There's no separate flag to opt into script- or agent-friendly behavior — the CLI is designed to run unattended by default:
- Terminals get tables, pipes get JSON. List responses render as aligned tables on an interactive terminal; piped or redirected output is always pretty-printed JSON, so anything reading the CLI's output — a script, a CI job, an agent — never has to parse table text.
- No interactive prompts. Every input is a flag,
-dpair, or piped body; there's nothing that blocks waiting on a keypress. - Errors are structured and exit non-zero. Non-2xx responses print the API's JSON:API error body and set a non-zero exit code, so failures are easy to detect in a pipeline.
- Auth doesn't require local state. Set
KLAVIYO_API_KEYand skipauth loginentirely — useful for CI and for agents that shouldn't touch your keychain.
A few flags round out a working script or agent call in a single command, with no wrapper code:
-
--jq <expr>filters any response through a built-in jq interpreter (gojq) — nojqinstall needed. Following jq convention, string results print raw and other values print as JSON, one result per line.klaviyo lists list --jq '.data[].attributes.name' klaviyo profiles list --paginate --jq '.data | length' -
--paginatefollowslinks.nextcursors and merges every page'sdatainto one response (GET list endpoints only). Combines with--jq, which then runs on the merged result. -
--revision <date>overrides the pinned API revision header for a single call. -
Anything newer than your CLI build is reachable with the raw escape hatch:
klaviyo api /api/metrics/ klaviyo api POST /api/events/ -d @event.json
Shell completion
Completion covers every command, flag, and configured account name:
# zsh (bash/fish/powershell also supported)
klaviyo completion zsh > "${fpath[1]}/_klaviyo"
Environment variables
| Variable | Purpose |
|---|---|
KLAVIYO_API_KEY | API key for requests, bypassing stored accounts (below --api-key in precedence) |
KLAVIYO_ACCOUNT | Named account to use (below --account in precedence) |
KLAVIYO_CONFIG_DIR | Config directory override (default ~/.config/klaviyo) |
KLAVIYO_NO_UPDATE_NOTIFIER | Disable the update check (also disabled automatically when CI is set) |
VISUAL, EDITOR | Editor used by klaviyo config -e |
Coming from the Headless Klaviyo CLI
The previous headless-klaviyo tool managed campaigns, flows, segments, and universal content blocks as local JSON files — get/generate/inspect/create/update/delete/report against files in KLAVIYO_CAMPAIGN_PATH-style directories. That model isn't part of this CLI: every command talks to the API directly, and nothing is cached to disk automatically.
If your workflow depended on a local copy of a resource, redirect a get command's output to a file yourself:
klaviyo campaigns get 01G5M1HWYX67DRW8AZEPWZAK9B > campaign.json
and edit or diff that file with the tools you'd already use for JSON — a text editor, jq, or diff.
Additional resources
Updated 10 days ago