The zeebra CLI

Use your workspace from the command line, or let an AI agent such as Claude Code or Codex use it, with one small script.

zeebra is a small command-line tool for the Zeebra API. Every API operation is a command, with the same checks and limits as the app. It suits scripts and AI coding agents (Claude Code, Codex, Cursor): they can run zeebra ops to see what's possible and zeebra <operation> --help to see how.

Install

It is one PHP file and needs PHP 8.2 or newer (macOS with Homebrew PHP, Linux, or Windows with WSL).

curl -fsSL https://zeebra.ai/cli/zeebra -o zeebra
chmod +x zeebra
sudo mv zeebra /usr/local/bin/   # or anywhere on your PATH

Log in

Create a full access API key in Settings → API, then:

zeebra login --url https://zeebra.ai

It asks for the key, checks it, and saves both in ~/.config/zeebra/config.json, readable only by you. zeebra logout removes them. In CI or for an agent you can set ZEEBRA_URL and ZEEBRA_API_KEY instead; they win over the saved login.

Run operations

zeebra ops                       # every operation, by section
zeebra ops campaign              # only the ones that mention "campaign"
zeebra list_contacts --help      # an operation's inputs
zeebra list_contacts --filter status=active --filter tag=VIP --per-page 50
zeebra get_contact --id 1234
zeebra create_campaign --name "November news" --format markdown --markdown @news.md --subject "What's new"
zeebra add_to_list --id 3 --contact-ids 12,13,14
zeebra create_webhook --json '{"url": "https://example.com/hooks/zeebra", "events": ["contact.unsubscribed"]}'
  • Inputs are --name value (or --name=value); dashes and underscores both work (--per-page, --per_page). A bare --flag is true.
  • @file reads a value from a file (--markdown @email.md, --csv @contacts.csv); @- reads standard input.
  • Lists take commas, repeats or JSON: --tags a,b, --tags a --tags b, --tags '["a","b"]'.
  • Objects take key=value (repeat it) or JSON: --filter status=active, --where '{"all": […]}'.
  • --json '{…}' (or --json @input.json) gives all inputs at once; --options next to it win.
  • Operation names work with dashes too: zeebra list-contacts.

Output

In a terminal, results are a table; when piped (or for an agent) they are the API's JSON ({"data": …, "meta": …}). Choose with -o json or -o table. Lists in pages say how to get the next one (--page 2).

Destructive operations

Operations that can't be undone (deleting, sending a campaign) ask first in a terminal. Without a terminal they need --yes. An agent should show you what it is about to do and only then add --yes. Sending a campaign also needs --confirm-recipients with the count from review_campaign, as in the API.

Exit codes

Code Meaning
0 It worked.
1 Another error, or you answered "no".
2 The command was mistyped (an unknown input, a missing one, a wrong type).
3 Not logged in, or the key was refused or can't do this (401, 403).
4 Not found (404).
5 Invalid input or a conflict, like a campaign that can't be sent in its state (422, 409).
6 Too many requests: up to 300 a minute per key (429).
7 Zeebra or the network had a problem (5xx, no connection).

Errors are printed to standard error as Error (code): message, with the inputs at fault.

Staying up to date

The list of operations comes from your Zeebra (https://zeebra.ai/cli/manifest.json) and is kept for an hour, so new operations show up without updating the CLI. zeebra ops --refresh fetches it now. If the CLI itself is too old for the server it says so; download it again with the same curl command.

For AI agents

Tell your agent to use zeebra, for example in CLAUDE.md or AGENTS.md:

Use the `zeebra` CLI for our email platform. Run `zeebra ops` to find an operation and
`zeebra <operation> --help` for its inputs. Output is JSON. Never add --yes or send a
campaign without asking me first.

Agents that speak MCP can also connect to the MCP server instead.