- 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/cliand 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
whoamibefore 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_contacttool should always attach anIdempotency-Keyso 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":"lead@acme.com","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), and429(back off, then retry). Onunsupported_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
Hard-won shortcuts that keep warm-up on track.
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.
Ready to put this into practice?
Start your FirstSales trial and launch a warmed, authenticated mailbox in minutes.
Start for $1