Skip to content
NewSee how

Using FirstSales with Gemini

Wire Gemini to FirstSales via the REST API with narrow function tools. Read before write, per-tool scopes, and safe, verifiable mutations.

  1. Two ways to wire up Gemini

    If your Gemini environment (e.g. the Gemini CLI with shell access) can run npm, install @firstsales.io/cli and expose it as a tool: it handles auth, JSON, and idempotency. If Gemini runs API-only, call the FirstSales Developer API over HTTPS from your function/tool implementations. The docs place Gemini with the API-first agents by default.

  2. Store the key outside the model

    Create a Developer API key in Settings → API and keep it in your app's server-side config as FIRSTSALES_API_KEY. Gemini should call a tool/function that reads the key server-side: the model itself must never see or emit the raw key.

    FIRSTSALES_API_KEY=fs-key-...
    FIRSTSALES_BASE_URL=https://api.app.firstsales.io
  3. Define narrow tools, not a raw HTTP escape hatch

    Give Gemini a small set of purpose-built functions (whoami, list_contacts, create_contact, campaign_analytics) each mapping to one public endpoint. This keeps the model on the supported surface instead of letting it construct arbitrary URLs.

    # what each tool calls under the hood
    GET  /api/v1/whoami
    GET  /api/v1/organizations/{org}/workspaces/{ws}/contacts
    POST /api/v1/organizations/{org}/workspaces/{ws}/contacts     # Idempotency-Key
    GET  /api/v1/organizations/{org}/workspaces/{ws}/campaigns/{id}/analytics
  4. Verify identity in the first tool call

    Have the agent call whoami before any write so it confirms the org and workspace context:

    curl https://api.app.firstsales.io/api/v1/whoami \
      -H "Authorization: Bearer $FIRSTSALES_API_KEY"
  5. Write idempotently from your create tool

    Your create_contact tool should always attach an Idempotency-Key so a retried function call can't double-write:

    curl -X POST \
      https://api.app.firstsales.io/api/v1/organizations/ORG_ID/workspaces/WS_ID/contacts \
      -H "Authorization: Bearer $FIRSTSALES_API_KEY" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: gemini-lead-2026-01" \
      -d '{"email":"[email protected]","first_name":"Sam"}'
  6. Handle errors and stay on the public surface

    In each tool, handle 401 (bad/revoked key), 422 (validation (surface the field), and 429 (back off, then retry). On unsupported_operation, return the limitation to the model and stop) never let Gemini scrape the app or hit private routes, callbacks, pixels, or cron endpoints.

Pro tips

  • Wrap endpoints as narrow tools

    Don't hand Gemini a generic 'call any URL' function. Purpose-built tools (create_contact, campaign_analytics) keep the model on the public surface and make its actions auditable.

  • The model never touches the key

    Read FIRSTSALES_API_KEY server-side inside the tool. If the key can appear in the model's context or output, it can leak into logs.

  • whoami as the first function call

    Make identity verification the opening move so Gemini always confirms org/workspace before it writes.

  • Shell available? The CLI is less code

    If the Gemini CLI has shell + npm, shelling out to firstsales beats hand-rolling HTTP tools: auth, JSON, idempotency, and delete-confirmation come for free.

Frequently asked questions

Does Gemini use the CLI or the API?

By default the Developer API, wrapped as functions/tools. If your Gemini environment has shell access with npm, the CLI is a lower-effort alternative that handles auth and idempotency for you.

How do I keep the key away from the model?

Store FIRSTSALES_API_KEY in server-side config and read it inside the tool implementation. The model calls the tool; it never sees or emits the raw key.

How should I structure the tools?

As narrow, single-purpose functions: one per public endpoint (whoami, list/create contact, campaign analytics). Avoid a generic 'fetch any URL' tool that lets the model leave the supported surface.

How do I avoid duplicate writes?

Attach an Idempotency-Key header inside your create tool. A retried function call with the same key returns the original result rather than creating a duplicate.

What's off-limits?

App-private routes, provider callbacks, tracking pixels, unsubscribe handlers, internal cron endpoints, and scraping the UI. Stay on public /api/v1; stop on unsupported_operation.

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.