Skip to content
NewSee how

FirstSales CLI Quickstart

Install the FirstSales CLI, authenticate with an API key, and run your first safe commands. Whoami, org/workspace discovery, dry-run, and idempotency.

  1. Install the CLI

    The FirstSales CLI is an npm package. Install it globally to get the firstsales binary:

    npm install -g @firstsales.io/cli
    firstsales --version
  2. Authenticate

    Create a Developer API key in Settings → API, then give the CLI the key and base URL. The cleanest way is environment variables:

    export FIRSTSALES_API_KEY="fs-key-..."
    export FIRSTSALES_BASE_URL="https://api.app.firstsales.io"
  3. Verify with whoami and doctor

    Before doing anything, confirm the key resolves to the right context. whoami shows your user, org, and workspace; doctor checks your setup:

    firstsales whoami --json
    firstsales doctor
  4. Understand the global flags

    Every command shares the same flags (learn these once:

    • --org <id> / --workspace <id>) target scope (most commands need both).
    • --json (default) / --pretty (output format.
    • --data '<json>' / --data-file <path>) request body.
    • --idempotency-key <key> (safe retries on creates.
    • --dry-run) preview the request without sending it.
    • --confirm (required for any destructive (delete) command.
    • --api-key / --base-url / --profile) override auth per call.
  5. Discover your org and workspace

    Most commands are scoped to an org and workspace. List them, then reuse the IDs:

    firstsales organizations list
    firstsales workspaces list --org ORG_ID
    firstsales campaigns list --org ORG_ID --workspace WS_ID
  6. Make a safe first mutation

    Follow the golden loop: inspect first, mutate deliberately, verify after. Preview with --dry-run, then create with an idempotency key, then re-read to confirm:

    # Preview: no write happens
    firstsales contacts create --org ORG_ID --workspace WS_ID \
      --data '{"email":"[email protected]","first_name":"Jane"}' --dry-run
    
    # Create for real, idempotently
    firstsales contacts create --org ORG_ID --workspace WS_ID \
      --data '{"email":"[email protected]","first_name":"Jane"}' \
      --idempotency-key jane-acme-2026-01
    
    # Verify
    firstsales contacts list --org ORG_ID --workspace WS_ID
  7. Delete deliberately

    Destructive commands refuse to run without --confirm: this is intentional friction so an agent or a fat-fingered command can't wipe data by accident:

    firstsales contacts delete --org ORG_ID --workspace WS_ID \
      --contact CONTACT_ID --confirm

Pro tips

  • Inspect first, mutate deliberately, verify after

    The CLI is built around this loop. List before you change, --dry-run the change, run it, then re-read to confirm. It's slower to type and far cheaper than undoing a bad write.

  • --json is for machines, --pretty is for you

    Output defaults to compact JSON so scripts and agents can parse it. Add --pretty when a human is reading, but keep --json in pipelines.

  • Never echo your key

    Pass auth via FIRSTSALES_API_KEY or a profile, not inline where it lands in shell history or logs. The CLI never prints raw keys: don't undo that by echoing the env var.

  • Idempotency keys are free insurance

    Any create that might be retried should carry --idempotency-key. A replayed command becomes a no-op instead of a duplicate.

Frequently asked questions

How do I install the CLI?

npm install -g @firstsales.io/cli, which gives you the firstsales binary. It maps directly to the public /api/v1 Developer API.

How does authentication work?

Set FIRSTSALES_API_KEY (and optionally FIRSTSALES_BASE_URL) in your environment, or pass --api-key / --base-url per command. Verify with firstsales whoami --json.

Why do commands need --org and --workspace?

Resources are scoped to an organization and workspace. List them with organizations list and workspaces list --org ORG_ID, then pass both IDs on scoped commands.

What does --dry-run do?

It prints the request the CLI would send without actually sending it: so you can confirm the body and target before a real mutation.

Why won't my delete run?

Destructive commands require the --confirm flag by design. Add --confirm once you're sure: it's the safety catch that stops accidental deletions.

Is the CLI different from the API?

It's a thin wrapper over the same public /api/v1 endpoints. The CLI adds convenience (centralized auth, stable JSON, idempotency handling, and destructive-op confirmation) so agents and scripts don't reimplement it.

Can I keep multiple accounts configured?

Yes: use --profile <name> to load a saved local profile, or override --api-key/--base-url per command to switch between orgs or environments.

Developer & CLI

Outreach that keeps going after you log off.Leads, emails, follow-ups and replies, handled.

Connect unlimited mailboxes and let AI agents find leads, write each email and follow up. Or send your own templates in manual mode. From $29/mo.

$1 for 3 days · Cancel anytime · No contract

Install with AI

Your coding agent sets up the FirstSales CLI and skills for you.

Prompt for Claude Code

I'm setting up FirstSales (https://firstsales.io), an AI outbound sales platform: campaigns, contacts, a reply inbox, deliverability and lead signals. Help me get my workspace sending real, good emails end to end, from Claude Code.

Read these first so you know how FirstSales works:
- Developer docs and API: https://developer.firstsales.io
- CLI: https://www.npmjs.com/package/@firstsales.io/cli
- Skills for coding agents: https://github.com/firstsalesio/firstsales-skills (read README.md and GUIDE.md)

Setup
1. Install the CLI: npm install -g @firstsales.io/cli
2. Install the FirstSales skills:
   claude plugin marketplace add firstsalesio/firstsales-skills
   claude plugin install firstsales@firstsales
3. Ask me if I already have a FirstSales account. If not, send me to https://app.firstsales.io to sign up.
4. Ask me to create an API key under Settings → API → API Keys, with only the scopes we need.
   Never ask me to paste the key into this chat. Tell me to run this in my own terminal, so the key stays hidden:
   read -rs FS_KEY && firstsales auth login --api-key "$FS_KEY"; unset FS_KEY
5. Run `firstsales whoami` and tell me which workspace and organisation we are connected to.

Then ask me about my business: what I sell, who I sell to (roles, company size, countries), my best customers, and what I use today for outbound (tools, lists, mailboxes). Walk me through:
1. Teaching FirstSales my business: website, offer, proof points and case studies in the knowledge base, so the AI writes like someone who knows the product.
2. Connecting my sending mailboxes and checking my sending domain: SPF, DKIM, DMARC and warm-up. If anything is unhealthy, tell me the exact DNS record to add.
3. Bringing in contacts: import my CSV or CRM export into a clean list, remove duplicates and bad emails, and block domains I must never email (customers, competitors, my own).
4. Building my first campaign: the right list, senders, steps and timing. Write the sequence with me, show me the drafts, and do not launch until I say yes.
5. Setting up Signals if it fits: find people engaging with LinkedIn posts about topics my buyers care about, and turn them into leads.
6. Inviting my team and giving each person the right access.

Once campaigns run, show me how to work the reply inbox from here: which replies need an answer, draft responses in my voice, and move good conversations into deals. If I use Cal.com, connect it so follow-ups can offer my booking link (not the first email).

Throughout, as you learn about my business, proactively suggest and help create:
- Reply templates for the questions and objections my prospects are likely to raise, based on my product, site and codebase if you can see them.
- Campaign ideas and segments worth testing, with the reason for each.

Safety rules:
- Show every change as a dry run (--dry-run) first and wait for my yes. Never send emails, launch campaigns or delete anything without my yes.
- Tell me before anything that spends credits, and how many.

Be opinionated. If the docs give a sensible default, just use it. Don't make me choose between five options.