- 1
Generate a Developer API key
In the app go to Settings → API and create a Developer API key. Set its scopes and access level (read vs read-write) to the minimum the integration needs. The secret is shown once — copy it into a secret manager immediately; the app only stores a redacted form.
Revoke and regenerate on the same page if a key ever leaks.
- 2
Verify identity with whoami
The base URL is
https://api.app.firstsales.io. Auth is a Bearer token. Always start by confirming the key resolves to the org and workspace you expect —GET /api/v1/whoami:curl https://api.app.firstsales.io/api/v1/whoami \ -H "Authorization: Bearer $FIRSTSALES_API_KEY" - 3
Find your org and workspace IDs
Every resource is scoped to an organization and a workspace, so most paths look like
/api/v1/organizations/{orgId}/workspaces/{workspaceId}/…. List them:# Organizations you can access curl https://api.app.firstsales.io/api/v1/organizations \ -H "Authorization: Bearer $FIRSTSALES_API_KEY" # Workspaces inside an org curl https://api.app.firstsales.io/api/v1/organizations/ORG_ID/workspaces \ -H "Authorization: Bearer $FIRSTSALES_API_KEY" - 4
Create a contact (idempotently)
POST a contact into a workspace. Pass an Idempotency-Key header so a retried request never creates a duplicate — safe to replay on network failure.
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: jane-acme-2026-01" \ -d '{ "email": "jane@acme.com", "first_name": "Jane", "company_name": "Acme" }' - 5
Read campaign analytics
Pull funnel and engagement metrics for a campaign to sync results into your own reporting — no scraping the dashboard:
curl https://api.app.firstsales.io/api/v1/organizations/ORG_ID/workspaces/WS_ID/campaigns/CAMPAIGN_ID/analytics \ -H "Authorization: Bearer $FIRSTSALES_API_KEY" - 6
Read replies from the inbox
List inbox threads to pull sends, opens, replies, and bounces into your systems. Thread bodies are redacted in the list view; fetch a single thread for detail. This is how you route positive replies into a CRM while native connectors are still coming.
curl https://api.app.firstsales.io/api/v1/organizations/ORG_ID/workspaces/WS_ID/inbox/threads \ -H "Authorization: Bearer $FIRSTSALES_API_KEY" - 7
Stay inside the public surface
The API only exposes the public
/api/v1surface. Don't call app-private routes, provider callbacks, tracking pixels, unsubscribe handlers, or internal cron routes — they aren't part of the contract and will break. If a call returnsunsupported_operation, stop and treat it as "not available via the API," not something to work around.Standard status codes apply:
401bad/revoked key,422validation (body names the field),429rate-limited (back off and retry).
Pro tips
Hard-won shortcuts that keep warm-up on track.
whoami before you write
Confirm the key resolves to the org/workspace you intend before any mutating call. A key scoped to the wrong workspace is the most common way to write data into the wrong place.
Always send an Idempotency-Key on creates
Network retries happen. An Idempotency-Key makes a replayed POST a no-op instead of a duplicate contact or double charge.
Store the key once, never print it
The secret is shown a single time and stored redacted. Put it straight into a secret manager; never log it, commit it, or ship it in a client bundle.
Treat unsupported_operation as a hard stop
If the API returns unsupported_operation, the thing you want isn't in the public contract. Don't reach for app-private routes to force it — report the limitation.
Frequently asked questions
Where do I get an API key?
Settings → API. Create a Developer API key with the scopes and access level you need; it's shown once, so copy it immediately. Revoke and regenerate on the same page if it's exposed.
What's the base URL and auth?
Base URL https://api.app.firstsales.io; authenticate with Authorization: Bearer $FIRSTSALES_API_KEY and send JSON. Start with GET /api/v1/whoami to confirm context.
Why are the paths so long?
Resources are scoped to an org and workspace, so paths are /api/v1/organizations/{orgId}/workspaces/{workspaceId}/…. Get the IDs from /api/v1/organizations and its /workspaces sub-resource (or from whoami).
How do I avoid duplicate writes?
Send an Idempotency-Key header on create requests. A retried request with the same key returns the original result instead of creating a second record — safe to replay after a timeout.
Can I read campaign metrics and replies?
Yes — GET …/campaigns/{id}/analytics for funnel/engagement, and GET …/inbox/threads for conversation activity. Thread bodies are redacted in the list; fetch a single thread for full detail.
Is there a Zapier or CRM integration instead?
No Zapier connector, and native CRM connectors (Salesforce, HubSpot, Pipedrive) are still coming. Until they ship, the REST API — or the CLI — is the supported way to integrate and sync.
What does unsupported_operation mean?
The requested action isn't part of the public /api/v1 contract. Stop and report it — don't call app-private routes, callbacks, pixels, or internal endpoints to work around it; those aren't supported and will break.
Ready to put this into practice?
Start your FirstSales trial and launch a warmed, authenticated mailbox in minutes.
Start for $1