Skip to Content
New: meet Clu, and send email, LinkedIn and call steps with Sequences. Watch the videos →
API Reference (v2)Authentication
🔌

Audience: developers. API keys need a Pro or Enterprise plan (legacy Pro and Scale plans included) or an AppSumo Tier 5, 6 or 7 license, and they never work during a trial. Support agent: confirm the user’s plan grants API access before giving API advice.

TL;DR: Every API v2 request uses Authorization: Bearer clapi_... (or a CluGTM CLI sign-in, or a Clerk session JWT). On top of the token, each endpoint checks one OAuth scope (for example enrich:write); a missing scope returns 403 insufficient_scope. Sends, CRM writes and deletes also need an opt-in scope ticked on the key. Create keys under Settings → API keys → Create API key. Up to 10 active keys per user per organization.

Authentication

CluGTM API v2 accepts three credential types:

CredentialHeaderUsed bywhoami.auth_type
API key (clapi_...)Authorization: Bearer clapi_...servers, scripts, your own agentsapi_key
CluGTM CLI sign-in (OAuth access token)Authorization: Bearer <token>the CluGTM CLI after cleanlist loginoauth
Clerk session JWTAuthorization: Bearer <jwt>the CluGTM app, and the /api/v1/api-keys management endpointsclerk_jwt

OAuth access tokens are refused with 401 and the message “OAuth access tokens are not accepted here. Use an API key.” The one exception is the CluGTM CLI: its token is accepted on /api/v2/ and /api/v1/public/ only, for the workspace picked at sign-in. It carries your scopes in that workspace, less the opt-in scopes, and needs the same plan as an API key. It has its own rate-limit bucket of 30 requests per minute for each user, like a key (see Rate limits). Each CLI token is checked with the sign-in service, at most a minute apart, so a sign-out (cleanlist logout) or a replaced token stops working within 60 seconds with 401 “This sign-in was revoked or replaced. Sign in again.”

Authorization: Bearer clapi_your_api_key_here

The CluGTM MCP for Claude doesn’t use an API key: it signs in with your CluGTM account through OAuth and carries the same scopes, less the opt-in ones. For a delete it asks you in the host app’s approval prompt instead.

Who can create API keys

PlanAPI keys
Pro, EnterpriseYes
Legacy Pro, legacy Scale (shown as Enterprise)Yes
AppSumo Tiers 5 to 9Yes
Free, Starter (current or legacy)No
AppSumo Tiers 1 to 4No
Any trial, including the Pro trialNo

On a plan without API access, the API keys page shows an upgrade card (“The CluGTM API.”) instead of the create form.

OAuth scopes

Authentication answers “who are you?”; scopes answer “what may you do?”. Every /api/v2 endpoint checks one scope before it runs. If your credential doesn’t carry it, the request fails with 403 and code insufficient_scope.

ScopeGrants
people:readPeople Finder, people filters, compile, find-at-companies, find-people status
people:writeReserved; no endpoint checks it today
companies:readCompany search, company filters, similar companies
companies:writeReserved; no endpoint checks it today
lists:readRead lead lists and their leads; read actions in the lists and skills groups
lists:writeCreate and update lists, add leads, CSV import, find-people jobs; other actions in the lists and skills groups. Deleting a list or removing leads also needs lists:delete
enrich:readPoll enrichment status
enrich:writeRun person, company, by-task and bulk enrichment
smart_agents:readList smart-agent runs and read results; read GTM agents, their runs and records; read actions in the agents and plays groups
smart_agents:writeRun a smart agent; create, edit and turn off GTM agents; other actions in the agents and plays groups
sync:writeSync a list to a CRM or sequencer
export:readCSV signed URLs and JSON export
credits:readCredit balance and cost estimates
admin:api_keysList keys and read usage reports through the API; manage webhook subscriptions
crm:readRead CRM records through CluGTM (CRM exclusions in company search, the CluGTM MCP); read actions in the crm group
sequences:readRead actions in the sequences group: sequences, mailboxes, LinkedIn accounts, a lead’s calls
sequences:writeOpt-in per key. Every other action in the sequences group: create and edit sequences, launch, pause and resume them, enroll people, send email and LinkedIn actions
crm:writeOpt-in per key. Every other action in the crm group: log a CRM task or note, merge, dismiss or skip duplicates, add field mappings
lists:deleteOpt-in per key. DELETE /lead-lists/{list_id}, DELETE /lead-lists/{list_id}/leads, and the lists.delete and lists.delete_leads actions

Your user has a set of scopes in each organization, and a key holds those scopes narrowed to the key’s own list. A key created without a list of its own (every key made before per-key scopes, and any key created with no boxes ticked) holds your scopes less the three opt-in scopes, so it can’t send, write to your CRM or delete lists and leads. A user granted the full set before crm:read and sequences:read existed receives both automatically, but never an opt-in scope.

The five legacy v1-only endpoints (/api/v1/public/auth/validate-key, /enrich/bulk, /enrich/status, /webhooks/deliveries, POST /folders) don’t check scopes. Every v2 endpoint does.

A 403 insufficient_scope error includes the required and granted scopes in details:

{ "error": { "code": "insufficient_scope", "problem": "This call requires the 'enrich:write' scope, which is not granted to the authenticating credential.", "fix": "Either re-issue the API key with the required scope, or sign in with an identity that has it. The full list of granted scopes is visible via /api/v2/whoami.", "retryable": false, "docs_url": "https://docs.cleanlist.ai/errors/insufficient-scope", "request_id": "req_...", "details": { "required_scope": "enrich:write", "granted_scopes": ["lists:read", "people:read"] } } }

To see what your credential can do, call GET /whoami; it returns the granted scopes list.

Opt-in scopes

Sends, CRM writes and deletes reach outside CluGTM or can’t be undone, so an API key holds their scopes only when someone opts in on that key:

Box in the appScopeLets the key
Send email and LinkedIn, and change sequencessequences:writeUse the sequences actions that aren’t reads
Write to your CRMcrm:writeUse the crm actions that aren’t reads
Delete lead lists and leadslists:deleteDelete lists and remove leads, by REST or by action

To opt in, open Settings → API keys. When you create a key, tick the boxes under This key may also. For an existing key, click Permissions on it under Your API keys, tick the boxes and click Save. Each key shows the opt-in scopes it holds. A key never holds more than your user’s scopes; with the full set, you can opt into all three.

Only a person signed in to CluGTM sets a key’s scopes. An API key, a CluGTM CLI sign-in or the CluGTM MCP that tries returns 403 “Set an API key’s scopes in CluGTM, signed in.”, so no credential can widen its own reach.

A call that needs an opt-in scope the key lacks fails with 403 opt_in_scope_required and details.required_scope, before anything runs:

{ "error": { "code": "opt_in_scope_required", "problem": "Deleting a list or its leads needs a key that opted into 'lists:delete'.", "fix": "Use a key with the 'lists:delete' scope turned on, or delete it in Cleanlist.", "retryable": false, "docs_url": "https://docs.cleanlist.ai/errors/opt-in-scope-required", "request_id": "req_...", "details": { "required_scope": "lists:delete" } } }

The opt-in scopes are set per API key. A CluGTM CLI browser or device sign-in doesn’t get them; to delete lists or leads from the CLI, sign in with an API key that has lists:delete (cleanlist login --api-key -). The CluGTM MCP asks the person instead: it runs a delete only after the person approves it in the host app’s own approval prompt.

Key format

PropertyValue
Prefixclapi_
OwnershipThe user who created it, in the organization it was created in
Secret body48 random URL-safe bytes
VisibilityShown once at creation; afterwards only the prefix and last 4 characters are displayed
Limit10 active keys per user per organization

The full key is only returned once, at creation. Store it in a secret manager, env var or vault. If you lose it, revoke it and create a new one.

Creating a key

The fastest path is the app:

  1. Go to Settings → API keys.
  2. Under Create API key, enter a Name (for example “CI integration”) and, if you want, an Expiry.
  3. If the key must send, write to your CRM or delete, tick those boxes under This key may also (see Opt-in scopes). Each is off unless you tick it.
  4. Click Create API key and copy the key shown under New API key.

Your keys are listed under Your API keys with their status and request count.

You can also create keys programmatically with your Clerk session token. These management endpoints live at /api/v1/api-keys (not under /public or /api/v2) and authenticate with the Clerk session JWT, not a clapi_ key.

Creating and listing keys, and changing their scopes, need API access on your plan (see Who can create API keys). Without it, POST /api/v1/api-keys returns 403 with code PERMISSION_DENIED, the message “This feature requires the Pro plan or higher.” and details.error: "feature_gated". Revoking keys and reading a key’s request log are not gated.

POST /api/v1/api-keys

curl -X POST https://api.cleanlist.ai/api/v1/api-keys \ -H "Authorization: Bearer <clerk_session_jwt>" \ -H "Content-Type: application/json" \ -d '{ "name": "Production server", "expires_at": "2027-01-01T00:00:00Z" }'

Request body

FieldTypeRequiredDescription
namestringNoA human-readable label
expires_atISO-8601 datetimeNoOptional expiration; omit for a non-expiring key
scopesstring[]NoThe key’s own scope list. The key holds your scopes narrowed to it, so include every scope the key needs, not only the opt-in ones. Omit it for your scopes less the opt-in scopes. An unknown scope returns 422 VALIDATION_ERROR naming it. Only an app session can send it; any other credential gets 403

Response

{ "api_key": "clapi_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "created_at": "2026-04-06T15:00:00Z", "expires_at": "2027-01-01T00:00:00Z" }

If you already have 10 active keys in this organization, creation returns 400 with “Maximum active API keys reached (10). Revoke an existing key before creating a new one.”

Listing your keys

GET /api/v1/api-keys

Lists your active and inactive keys in the current organization (Clerk session JWT, needs API access on the plan). The full secret is never returned, only the first 10 characters (key_prefix) and last 4 (key_last4).

curl https://api.cleanlist.ai/api/v1/api-keys \ -H "Authorization: Bearer <clerk_session_jwt>"
[ { "id": "550e8400-e29b-41d4-a716-446655440000", "name": "Production server", "key_prefix": "clapi_AbCd", "key_last4": "wXyZ", "created_at": "2026-04-06T15:00:00Z", "expires_at": null, "last_used_at": "2026-04-06T15:42:11Z", "request_count": 1547, "is_active": true, "scopes": null } ]

scopes is the key’s own list, or null for a key that has none: it holds your scopes less the opt-in scopes.

There is also a clapi_-authenticated GET /api/v2/api-keys (scope admin:api_keys) that returns key_id, key_prefix, key_last4, name, scopes, created_at, last_used_at and is_active for each key. Its scopes field shows the calling credential’s scopes on every key, not each key’s own; read a key’s own list from GET /api/v1/api-keys, or call GET /whoami with that key.

Changing a key’s scopes

In the app, click Permissions on the key under Your API keys, tick or clear the boxes, and click Save.

PUT /api/v1/api-keys/{key_id}/scopes

Sets one of your active keys’ scope list (Clerk session JWT only).

curl -X PUT https://api.cleanlist.ai/api/v1/api-keys/550e8400-e29b-41d4-a716-446655440000/scopes \ -H "Authorization: Bearer <clerk_session_jwt>" \ -H "Content-Type: application/json" \ -d '{"scopes": ["people:read", "lists:read", "lists:write", "lists:delete"]}'

The body is {"scopes": [...]}, with the same rules as scopes on create. {"scopes": null} puts the key back on your scopes less the opt-in scopes. The response is the key, as GET /api/v1/api-keys lists it. A key that isn’t yours, or isn’t active, returns 404 “Active API key not found”.

Revoking keys

In the app, open Settings → API keys, find the key under Your API keys and click Revoke, or click Revoke all.

POST /api/v1/api-keys/{key_id}/revoke

Revoke a single key by its UUID:

curl -X POST https://api.cleanlist.ai/api/v1/api-keys/550e8400-e29b-41d4-a716-446655440000/revoke \ -H "Authorization: Bearer <clerk_session_jwt>"

POST /api/v1/api-keys/revoke-all

Revoke every active key for the authenticated user (useful if you suspect a leak):

curl -X POST https://api.cleanlist.ai/api/v1/api-keys/revoke-all \ -H "Authorization: Bearer <clerk_session_jwt>"
{ "success": true, "message": "3 API key(s) revoked successfully" }

Revoked keys can’t be reactivated; create a new one.

Inspecting key usage

In the app, click View requests on a key under Your API keys.

GET /api/v1/api-keys/{key_id}/requests

Pull the most recent requests made with a specific key:

curl "https://api.cleanlist.ai/api/v1/api-keys/550e8400-e29b-41d4-a716-446655440000/requests?limit=100" \ -H "Authorization: Bearer <clerk_session_jwt>"
Query paramDefaultRange
limit1001 to 500
[ { "id": "log-uuid", "method": "POST", "path": "/api/v2/enrichment/bulk", "status_code": 200, "duration_ms": 184, "ip_address": "203.0.113.42", "user_agent": "python-requests/2.32.0", "created_at": "2026-04-06T15:42:11Z" } ]

Using a key

Once you have a clapi_ key, every API v2 call uses the same Bearer header:

curl https://api.cleanlist.ai/api/v2/credits/balance \ -H "Authorization: Bearer clapi_your_api_key"
🧑‍💻

With an SDK: you don’t build this header yourself. Pass your key to the client (Cleanlist(access_token="clapi_…"), or the CLEANLIST_API_KEY env var) and every call is authenticated. See the SDKs.

Verifying a key

To confirm a key is valid (and inspect its scopes and tier) without spending credits, call GET /whoami:

curl https://api.cleanlist.ai/api/v2/whoami \ -H "Authorization: Bearer clapi_your_api_key"

A 401 means the key is missing, malformed, expired or revoked.

Security best practices

  • Never commit clapi_ keys to source control.
  • Store keys in a secret manager (AWS Secrets Manager, Doppler, 1Password, Vault and similar).
  • Use a Name and an Expiry to make keys easy to rotate and audit.
  • If a key leaks, revoke it at once (Revoke in the app, or POST /api/v1/api-keys/{key_id}/revoke).
  • Watch the request count and View requests log for unexpected use.
  • Use one key per environment (dev, staging, production), and never share keys across systems.

Common authentication errors

StatusCodeCauseFix
401AUTHENTICATION_INVALIDMissing, malformed, expired or revoked key (“Invalid or expired API key”, or “Not authenticated” when the header is missing)Check the header format and the key’s status under Your API keys
403insufficient_scopeCredential lacks the endpoint’s scopeCheck /whoami; if a scope you need is missing, contact support@cleanlist.ai
403opt_in_scope_requiredThe call sends, writes to your CRM or deletes, and the key hasn’t opted into details.required_scopeTick it on the key under Settings → API keys → Permissions (Opt-in scopes)
401AUTHENTICATION_INVALIDA CluGTM CLI sign-in that was signed out or replaced (“This sign-in was revoked or replaced. Sign in again.”)Run cleanlist login again
401AUTHENTICATION_INVALIDA CluGTM CLI sign-in that couldn’t be checked just then (“Couldn’t confirm this sign-in right now. Try again shortly.”), with Retry-After: 5Retry after 5 seconds
403PERMISSION_DENIED (details.error: feature_gated)Plan has no API access (key creation or listing)Move to Pro or Enterprise under Settings → Plans & billing
401AUTHENTICATION_INVALIDAn OAuth access token other than the CluGTM CLI’s, or the CLI token on a non-public route (“OAuth access tokens are not accepted here. Use an API key.”)Use a clapi_ key, or sign in with the CluGTM CLI
403PERMISSION_DENIEDKey used while a trial is active (“Public API access is not included during the trial…”)Use the app or the MCP during the trial, or start a paid plan
403PERMISSION_DENIEDKey or CLI sign-in used on a plan without API access, for example after a trial ended (“Public API access is not included in the current plan. Choose an eligible plan to use the public API.”)Move to Pro or Enterprise under Settings → Plans & billing
429RATE_LIMITEDRate limit hitBack off using Retry-After; see Errors

Authentication, plan and rate-limit errors use the generic error shape (code + message); see Errors & Rate Limits.

Learn more