# CLI Quickstart

> Install the official sendping CLI and send your first email from the terminal in three commands.

SendPing ships an official command-line tool. `sendping-cli` (**5.1.1** on npm) installs a **`sendping`** command that covers the whole API — sends, domains, contacts, segments, topics, campaigns, templates, automations, webhooks, events, logs and polls. It wraps the official [`sendping` Node.js SDK](https://www.sendping.co/docs/resources/sdks), so a CLI call and an SDK call hit exactly the same endpoints.

Every command prints the API response as JSON on stdout, which makes the CLI both the quickest way to try an endpoint by hand and a safe building block in scripts and CI.

## Prerequisites

- **Node.js 18 or newer** — `sendping-cli` declares `engines.node: ">=18"`.
- A SendPing [API key](https://www.sendping.co/docs/create-an-api-key) — the `mb_`-prefixed secret you create in the dashboard.
- A [verified domain](https://www.sendping.co/docs/domains/managing) to send from.

## 1. Install the CLI

**Install globally**

```bash
npm install -g sendping-cli

# the package is sendping-cli; the command it installs is sendping
sendping --version
# 5.1.1
```

Rather not install anything? Run it straight from npm — name both the package and the binary, because they differ:

```bash
npx --package sendping-cli@5.1.1 sendping --version
```

`--help` works at every level: `sendping --help` lists the resource groups, `sendping emails --help` lists that group's subcommands, and `sendping emails send --help` lists every flag.

## 2. Authenticate

There is no `login` step. Every invocation that talks to the API resolves your key from the `SENDPING_API_KEY` environment variable, or from `--api-key` on that command; with neither, the CLI exits `1` with a `cli_error`. The one exception is `sendping webhooks verify`, which checks a signature locally, makes no request, and therefore takes no key — passing `--api-key` to it fails with an unknown-option error. Export your key once so it stays out of your shell history, then prove it works:

```bash
export SENDPING_API_KEY=mb_xxxxxxxxx

# any command exercises the key — this one lists your sending domains
sendping domains list
```

Pass `--base-url` (or set `SENDPING_BASE_URL`) to point the CLI at a different API host.

> **Warning:** Never paste a live key into a command you will commit or share — keep it in an environment variable or your CI secret store. Key lifecycle is dashboard-only (`sendping api-keys list` is the CLI's entire key surface), so a leaked key cannot mint, widen, or revoke another. See [API keys](https://www.sendping.co/docs/api-keys/overview).

## 3. Send your first email

Send from your verified domain to `delivered@test.sendping.co`, the [mailbox simulator](https://www.sendping.co/docs/kb/test-addresses): it is intercepted before the provider is contacted and synthesizes a delivery, so nothing reaches a real inbox. `--to`, `--cc`, `--bcc` and `--reply-to` are repeatable and also accept comma-separated values.

**sendping emails send**

```bash
sendping emails send \
  --from 'Acme <hello@yourdomain.com>' \
  --to delivered@test.sendping.co \
  --subject 'Hello from the CLI' \
  --html '<p>Your first email 🎉</p>'
```

A successful send prints the new email id — the same body [POST /emails](https://www.sendping.co/docs/api/emails-send) returns:

```json
{
  "id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"
}
```

> **Warning:** Use the simulator, not a documentation domain: `example.com`, `example.net`, `example.org` and anything under `.test`, `.invalid`, `.localhost` or `.example` are blocked outright and answer `422`. Simulator sends still debit your quota per recipient, exactly like a real send.

## 4. See what happened

Read the email back by id for its current status and ordered event log, or list your recent sends:

```bash
sendping emails get 49a3999c-0ce1-4ea6-ab68-afcd6dc2e794
sendping emails list --limit 5
sendping emails list --status delivered --search invoice
sendping emails list --folder sent --limit 5
```

**emails get (trimmed)**

```json
{
  "object": "email",
  "id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794",
  "from": "Acme <hello@yourdomain.com>",
  "to": ["delivered@test.sendping.co"],
  "subject": "Hello from the CLI",
  "status": "delivered",
  "last_event": "delivered",
  "events": [
    { "type": "sent", "created_at": "2026-06-23T10:00:00.000Z" },
    { "type": "delivered", "created_at": "2026-06-23T10:00:04.512Z" }
  ]
}
```

## Output and exit codes

Responses are pretty-printed JSON by default; `--json` switches to compact JSON for piping into `jq`. stdout stays JSON-only either way — a failure prints its error object to **stderr** and exits `1`, so `sendping … | jq` never has to strip a diagnostic.

```bash
id=$(sendping emails send --json \
  --from 'Acme <hello@yourdomain.com>' \
  --to delivered@test.sendping.co \
  --subject 'Hello from the CLI' \
  --text 'It works!' | jq -r '.id') || exit 1
```

The error object is always `{ statusCode, name, message }`. Branch on `name`, never on `message`:

| Where it came from | `statusCode` | `name` |
| --- | --- | --- |
| The API rejected the request | the HTTP status | the API's reason, e.g. `validation_error`, `daily_quota_exceeded` |
| The request never reached the API | `0` | `network_error` |
| The CLI rejected your flags before sending | `null` | `cli_error` |

> **Note:** See the [error reference](https://www.sendping.co/docs/api/errors) for every name the API can return.

## Beyond the first send

A few flags on `emails send` worth knowing early:

- `--text` alongside (or instead of) `--html`, plus `--preview-text` for the inbox preheader.
- `--template-id` or `--template-alias` with `--variables '{"first_name":"Ada"}'` to send a stored [template](https://www.sendping.co/docs/templates/overview).
- `--attachment ./invoice.pdf` (read and base64-encoded locally) or `--attachment-url` (fetched server-side) — both repeatable, capped at 25 MB per file and 40 MB per message. See [Attachments](https://www.sendping.co/docs/emails/attachments).
- `--idempotency-key order-12345` on `emails send` and `emails batch` so a retry cannot send twice. See [Idempotency keys](https://www.sendping.co/docs/emails/idempotency).
- `sendping emails batch --file ./batch.json` to send up to 100 emails in one request. See [Batch sending](https://www.sendping.co/docs/emails/batch).

Scheduling uses `--scheduled-at` — an ISO 8601 timestamp or a phrase like `in 1 min`, up to 30 days ahead. A scheduled message skips the mailbox simulator, so schedule to a real address you control instead:

```bash
sendping emails send \
  --from 'Acme <hello@yourdomain.com>' \
  --to you@yourdomain.com \
  --subject 'Your weekly digest' \
  --html '<p>Here is what you missed.</p>' \
  --scheduled-at 2026-09-01T09:00:00Z

# reschedule or cancel while it is still pending
sendping emails update 49a3999c-0ce1-4ea6-ab68-afcd6dc2e794 --scheduled-at 'in 2 hours'
sendping emails cancel 49a3999c-0ce1-4ea6-ab68-afcd6dc2e794
```

> **Warning:** Do not pair `delivered@test.sendping.co` with `--scheduled-at`: the simulator only fires on an immediate send, so a scheduled message to it is treated as suppressed and rejected with `422`. See [Schedule emails](https://www.sendping.co/docs/emails/schedule).

## Next steps

- The full command surface, plus the equivalent `curl` cookbook: [CLI](https://www.sendping.co/docs/resources/cli).
- Non-interactive patterns for agents and CI — stdin piping, batches, safe retries, webhook feedback loops: [CLI for AI agents](https://www.sendping.co/docs/resources/cli-agents).
- A typed client instead of a shell: the official [SDKs](https://www.sendping.co/docs/resources/sdks) for Node.js, Python, Go, Ruby, PHP, Rust, Java and .NET.
- Every endpoint and field: the [API reference](https://www.sendping.co/docs/api/emails-send).
