---
title: "Using FirstSales with Claude Code | FirstSales"
description: "Drive FirstSales from Claude Code through the CLI — install, authenticate, inspect before mutating, and never expose your API key in the agent's output."
canonical: "https://firstsales.io/tutorial/firstsales-with-claude-code/"
---

[Home](/)/[Tutorials](/tutorial/)/Using FirstSales with Claude Code

Developer & CLI

# Using FirstSales with Claude Code

Drive FirstSales from Claude Code through the CLI — install, authenticate, inspect before mutating, and never expose your API key in the agent's output.

8 min read·Advanced·7 steps

1. 1  
## Why the CLI for Claude Code  
Claude Code has shell access, so it should drive FirstSales through the **CLI**, not raw HTTP. The CLI centralizes authentication, returns stable JSON the agent can parse, handles idempotency headers, and forces confirmation on destructive operations — everything you'd otherwise have to prompt the agent to do correctly every time.  
Rule of thumb from the docs: _agents should prefer the CLI when shell access exists._
2. 2  
## Install and expose the key  
Install the CLI once, and give Claude Code the key through the environment so it never appears in a prompt or transcript:  
```  
npm install -g @firstsales.io/cli  
export FIRSTSALES_API_KEY="fs-key-..."   # set in your shell, not in the chat  
```
3. 3  
## Give the agent its operating rules  
Paste a short operating contract into Claude Code (or a project rules file) so it behaves safely:  
   * Start every session with `firstsales whoami --json`; confirm org and workspace before acting.  
   * **Inspect first, mutate deliberately, verify after.**  
   * Use `--idempotency-key` on any create that might be retried.  
   * Use `--confirm` only for an intentional delete the user asked for.  
   * **Never print raw API keys.**
4. 4  
## Read-only exploration is always safe  
Encourage the agent to explore with list/get commands freely — they can't damage anything:  
```  
firstsales campaigns list --org ORG_ID --workspace WS_ID --json  
firstsales contacts list --org ORG_ID --workspace WS_ID --json  
firstsales billing overview --org ORG_ID --json  
```
5. 5  
## Mutations: preview, act, verify  
For anything that writes, have Claude Code follow the loop end to end — `--dry-run`, then the real call with an idempotency key, then a re-read:  
```  
firstsales contacts create --org ORG_ID --workspace WS_ID \  
  --data '{"email":"lead@acme.com","first_name":"Sam"}' --dry-run  
firstsales contacts create --org ORG_ID --workspace WS_ID \  
  --data '{"email":"lead@acme.com","first_name":"Sam"}' \  
  --idempotency-key sam-acme-2026-01  
firstsales contacts list --org ORG_ID --workspace WS_ID --json  
```
6. 6  
## Approve or reject AI drafts from the terminal  
A useful Claude Code loop: review pending AI-written replies and act on them without opening the app. List threads, read one, then approve or reject the draft:  
```  
firstsales inbox threads --org ORG_ID --workspace WS_ID  
firstsales inbox thread --org ORG_ID --workspace WS_ID --thread THREAD_ID  
firstsales inbox approve-draft --org ORG_ID --workspace WS_ID \  
  --email EMAIL_ID --idempotency-key approve-EMAIL_ID  
```
7. 7  
## Respect the public surface  
Tell the agent to stay on the CLI's public commands. If a command returns `unsupported_operation`, it must **stop and report** — not try to call app-private routes, provider callbacks, tracking pixels, unsubscribe handlers, or internal cron endpoints to force the result.

## Pro tips

Hard-won shortcuts that keep warm-up on track.

1

### Put the operating rules in a project rules file

Rather than re-pasting the safety contract each session, drop it in your Claude Code project rules so every run starts with whoami and the inspect-mutate-verify loop.

2

### Key in the environment, never in the chat

Export FIRSTSALES\_API\_KEY in the shell Claude Code runs in. If the key is in a prompt, it's in the transcript — the CLI keeps it out of output, don't put it back in.

3

### Let it read freely, gate the writes

list/get/analytics commands are safe to run unsupervised. Reserve your review for anything with create/update/delete or --confirm.

4

### unsupported\_operation means stop

Train the agent to treat unsupported\_operation as a wall, not a puzzle. The public CLI is the whole allowed surface; there is no clever workaround worth the risk.

## Frequently asked questions

Should Claude Code use the CLI or the API?

The **CLI**. Because Claude Code has shell access, the CLI is preferred — it centralizes auth, emits stable JSON, and enforces idempotency and destructive-op confirmation so the agent doesn't have to reimplement those.

How do I keep my API key out of the transcript?

Export `FIRSTSALES_API_KEY` in the shell before starting Claude Code, and instruct it never to print raw keys. The CLI reads the env var and never echoes it in output.

What's the safe workflow for changes?

Verify identity with `whoami`, confirm org/workspace, inspect current state, make the minimal change with `--dry-run` then `--idempotency-key`, use `--confirm` only for intended deletes, and re-read to validate.

Can it manage the inbox?

Yes — `inbox threads`, `inbox thread`, and `inbox approve-draft` / `reject-draft` let Claude Code triage and act on AI-written replies from the terminal.

What if a command isn't supported?

If the CLI returns `unsupported_operation`, the agent should stop and report it. It must not fall back to app-private routes, callbacks, pixels, or cron endpoints — those are off-limits and will break.

How do I stop accidental deletions?

Destructive commands require `--confirm`. As long as the agent only adds `--confirm` for deletions the user explicitly asked for, an accidental wipe can't happen silently.

## Ready to put this into practice?

Start your FirstSales trial and launch a warmed, authenticated mailbox in minutes.

[Start for $1](https://app.firstsales.io)

[All tutorials](/tutorial/)