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: GET /actions lists the Clu tools your credential can call, by group (lists, skills, agents, plays, sequences, crm), each with an input_schema. POST /actions/{name} runs one. A read or write answers with its result; anything that spends, sends, deletes or writes to a CRM answers with a card and runs nothing until you confirm it with POST /actions/confirm (or send commit: true). Sends, CRM writes and deletes also need a key that opted into sequences:write, crm:write or lists:delete.
Actions
Actions are the tools Clu uses in the app, callable from code by a stable public name such as lists.rename, skills.run_on_list or sequences.enroll. Each runs through the same toolkit Clu uses, in your workspace, with your data and credit balance.
| Endpoint | Method | Scope |
|---|---|---|
| List actions | GET /actions | none; the list is filtered by your scopes |
| Call an action | POST /actions/{name} | The action’s own scope |
| Confirm a card | POST /actions/confirm | The action’s own scope, plus its opt-in |
These endpoints are in the served OpenAPI spec but not in the SDKs yet. The CluGTM CLI wraps them as cleanlist actions list|run|confirm.
List actions
GET /actions
Returns the actions this credential can use, after the plan, connection and scope checks, plus the groups they fall in.
curl https://api.cleanlist.ai/api/v2/actions \
-H "Authorization: Bearer clapi_your_api_key"{
"version": "5d2c...",
"groups": [
{
"name": "lists",
"title": "Lead lists",
"summary": "Read, create, rename, import into, move and delete lead lists and their leads. 2 of its actions need lists:delete, which this credential doesn't hold, so they aren't listed.",
"action_count": 6,
"missing_scopes": ["lists:delete"]
}
],
"actions": [
{
"name": "lists.rename",
"title": "Rename a list",
"description": "...",
"group": "lists",
"effect": "write",
"card": false,
"scope": "lists:write",
"input_schema": { "type": "object", "properties": { "...": {} }, "required": ["..."] }
}
]
}| Field | Meaning |
|---|---|
version | A hash of the action list. It changes when the list does |
groups[].missing_scopes | Scopes this credential lacks for the group’s other actions. Those actions aren’t listed, and summary says so |
actions[].effect | read or write for an action that runs when called. For a card: spend, send, internal or crm_write |
actions[].card | true when the action returns a card instead of running |
actions[].scope | The scope the action checks: the group’s read scope for a read, its write scope otherwise |
actions[].input_schema | JSON Schema for arguments. An action that works on one agent requires agent_id (from agents.list) |
An action your plan doesn’t include, or one that needs a connection you haven’t made, isn’t listed. GET /actions is the source of truth; the tables below show what exists.
Groups and scopes
| Group | What it covers | Read scope | Write scope |
|---|---|---|---|
lists | Read, create, rename, import into, move and delete lead lists and their leads | lists:read | lists:write |
skills | Create and change skills and record skills, add columns to a list, run them on its leads | lists:read | lists:write |
agents | List, draft, update, schedule, turn on or off and run GTM agents, read their runs and outcomes | smart_agents:read | smart_agents:write |
plays | Build an agent’s play: steps, fields, audience, the requirement review, the proposal, a test run, plays run once | smart_agents:read | smart_agents:write |
sequences | Create, edit, launch and pause sequences, enroll people, send email and LinkedIn actions | sequences:read | sequences:write (opt-in) |
crm | Read, search and match CRM records and activity, log an activity, map fields, review and merge duplicates | crm:read | crm:write (opt-in) |
sequences:write and crm:write are opt-in scopes: every action in those groups that isn’t a read needs a key that opted into them. lists.delete and lists.delete_leads check lists:write and also need the lists:delete opt-in.
The actions
| Group | Run when called | Return a card |
|---|---|---|
lists | lists.list, lists.get_leads, lists.create, lists.rename | lists.import, lists.move_leads, lists.delete†, lists.delete_leads† |
skills | skills.list, skills.get, skills.list_columns, skills.create, skills.attach, skills.detach, skills.create_record_skill, skills.update_record_skill, skills.add_column | skills.set_column_values, skills.run_on_list, skills.run_smart_column |
agents | agents.list, agents.list_saved, agents.get, agents.list_runs, agents.get_run, agents.get_run_records, agents.outcomes, agents.list_connections, agents.list_secrets, agents.create_draft, agents.update, agents.set_schedule | agents.run_sample, agents.set_status‡, agents.set_limits‡, agents.run_now‡, agents.approve_try‡ |
plays | plays.describe_steps, plays.get_fields, plays.describe_audience_filters, plays.size_audience, plays.lookup_filter_values, plays.describe_sheet, plays.describe_notion_database, plays.list_sequencer_sequences, plays.list_sequencer_users, plays.get_webhook_samples, plays.crm_scan_status, plays.list_crm_values, plays.describe_crm_object, plays.list_crm_views, plays.draft_spec, plays.check_build, plays.check_configuration, plays.find_test_records, plays.get_run_results, plays.build_from_spec, plays.propose_changes, plays.discard_proposal, plays.run_test, plays.create_webhook_endpoint | plays.review_requirements, plays.accept_proposal, plays.run_once‡, plays.repeat‡ |
sequences | sequences.list, sequences.get, sequences.list_mailboxes, sequences.list_linkedin_accounts, sequences.list_lead_calls, sequences.create† | sequences.edit†, sequences.set_status†, sequences.enroll†, sequences.send_email†, sequences.linkedin_action†, sequences.post_comment† |
crm | crm.match_people, crm.read_account, crm.search_records, crm.read_record, crm.count_activity, crm.list_activity_fields, crm.list_users, crm.read_integrations, crm.read_6sense_scores, crm.find_duplicates, crm.read_duplicate_group, crm.dismiss_duplicates†, crm.skip_duplicates† | crm.create_activity†, crm.merge_duplicates†, crm.add_field_mappings† |
† Needs a key that opted into the group’s opt-in scope (lists:delete, sequences:write or crm:write).
‡ A person only: code can get the card but can’t confirm it (403 person_required).
Call an action
POST /actions/{name}
| Field | Type | Notes |
|---|---|---|
arguments | object | The action’s arguments, as its input_schema describes. Default {} |
session_id | string | The session_id from an earlier call. Leave it out and a new session is created and returned |
commit | boolean | Code only. true confirms a card in the same call. Default false: a card is a preview and nothing runs |
curl -X POST https://api.cleanlist.ai/api/v2/actions/lists.rename \
-H "Authorization: Bearer clapi_your_api_key" \
-H "Content-Type: application/json" \
-d '{"arguments": {"list_id": "9c1f7e2a-...", "new_name": "Q4 ABM"}}'Read each action’s argument names from its input_schema.
An action that runs answers with its result:
{
"action": "lists.rename",
"session_id": "0b8e6c1a-...",
"kind": "result",
"result": { "...": "..." },
"card": null,
"confirmed": null
}A card action answers with kind: "card" and runs nothing:
{
"action": "lists.delete",
"session_id": "0b8e6c1a-...",
"kind": "card",
"result": null,
"card": {
"card_id": "5f2b9c0e7d4a4b1f8e6a3c2d1b0a9f8e",
"title": "Delete a list",
"summary": "... Confirming it adds the 0.2-credit action fee.",
"credits_required": 0.2,
"expires_at": "2026-11-12T15:10:00+00:00"
},
"confirmed": null
}Show the card to a person: title, summary and credits_required say what will happen and what it costs. A card that names a lead list is refused with 404 list_not_found before it’s issued if the list isn’t in your workspace. Every issued card also sends an approval.needed event to your webhooks.
Reads stop after 60 seconds (504 action_timeout). A write runs to the end, so it’s never left half done.
Confirm a card
POST /actions/confirm
| Field | Type | Notes |
|---|---|---|
card_id | string | Required. From the card |
session_id | string | Required. The session the card was issued in |
curl -X POST https://api.cleanlist.ai/api/v2/actions/confirm \
-H "Authorization: Bearer clapi_your_api_key" \
-H "Content-Type: application/json" \
-d '{"card_id": "5f2b9c0e7d4a4b1f8e6a3c2d1b0a9f8e", "session_id": "0b8e6c1a-..."}'{ "card_id": "5f2b9c0e7d4a4b1f8e6a3c2d1b0a9f8e", "status": "done", "result": { "...": "..." } }status is done or failed. A card runs exactly the arguments it was issued with, once:
- A card confirms only on the channel that issued it. A card from the CluGTM MCP can’t be confirmed with an API key, and the reverse (
404 card_not_found). - A card past its
expires_atreturns410 card_expired. Call the action again. - Confirming the same card again, for example after a dropped connection, returns its first result. If it’s still running you get
409 card_already_used; confirm again in a minute to read the result.
To issue and confirm in one call, send "commit": true on POST /actions/{name}. The response then has the card and, in confirmed, the same body POST /actions/confirm returns. The same rules apply.
Who can confirm what
| Card | From code (API key, CLI) | From the CluGTM MCP |
|---|---|---|
| Spends credits or changes data in CluGTM (an import, a skill run, a play proposal) | The action’s scope | The person’s yes in the chat |
| Sends, CRM writes and deletes | The key’s opt-in scope: sequences:write, crm:write or lists:delete | The person approves in the host app’s own approval prompt |
| Turns an agent on, runs it now, changes its limits, runs or repeats a play, approves a Try | Never: 403 person_required | The person approves in the host app’s own approval prompt |
From code, a key without the opt-in is refused before a card is issued, with 403 opt_in_scope_required and details.required_scope. The person ticks it on the key under Settings → API keys → Permissions (see Opt-in scopes).
Cost
Each call is metered like the same tool called from Clu, on its own usage receipt. A card’s credits_required is the work’s estimate plus the 0.2-credit action fee that confirming it charges, and its summary says so. If the balance can’t cover a call you get 402 insufficient_credits.
Errors
| Code | HTTP | What to do |
|---|---|---|
action_not_found | 404 | No action by that name for this credential, or it isn’t available to this workspace. List them with GET /actions |
insufficient_scope | 403 | The credential lacks the action’s scope |
opt_in_scope_required | 403 | The key hasn’t opted into details.required_scope. Tick it on the key, or do it in CluGTM |
person_required | 403 | A person must confirm this card in CluGTM |
plan_required | 403 | The plan doesn’t include this action. Upgrade, then call again |
invalid_arguments | 422 | The arguments don’t match the action’s input_schema |
list_not_found, agent_not_found | 404 | The list or agent isn’t in this workspace |
session_not_found | 404 | No such session for this caller. Leave session_id out to start one |
card_not_found | 404 | No such card for this caller and session, or it was issued on the other channel |
card_expired | 410 | Call the action again for a new card |
card_already_used | 409 | The card is still running. Confirm again in a minute to read its result |
session_busy | 409 | Another call is running in this session. Retry when it ends |
action_not_rerun | 409 | This call already ran and won’t run twice |
insufficient_credits | 402 | The balance can’t cover the call |
budget_stopped | 402 | The call doesn’t fit the session’s tool-call or credit budget |
too_many_running | 429 | Too many calls are running in this workspace. Retry shortly |
action_timeout | 504 | A read ran past 60 seconds. Retry with a narrower request |
plan_unavailable, metering_unavailable | 503 | Retry shortly |
See Errors & Rate Limits for every code.