Documentation

Kairon CLI

Search LinkedIn, build lists, run campaigns and answer replies from a terminal, a script or a cron job. It is an MCP client, not a second API, so a command and an assistant make the same call.

Installnpm i -g @kairon/cli

Getting started

  1. Step 1

    Install it

    Needs Node 22.23.1 or newer. You can also run it once with npx and install nothing.

    npm i -g @kairon/clinpx @kairon/cli whoami
  2. Step 2

    Sign in

    Opens your browser and signs you in with your Kairon account. There is no API key. Credentials are stored in ~/.kairon/config.json at mode 0600, keyed by server, so a local login never disturbs the production one.

    kairon loginkairon login --server http://localhost:3055
  3. Step 3

    Check who you are

    Reports the account and the LinkedIn seat every command will run as. If this works, the rest will.

    kairon whoami
  4. Step 4

    Run something

    Every command prints JSON, so it pipes straight into jq. Ask for help on any group or command at any time.

    kairon chat list --unreadkairon list --help

Commands

One command per tool, and there is no list of them in the CLI itself. A tool name splits on its first underscore and the rest is kebab-cased, so the surface here is exactly the surface the server exposes.

This page is generated from the tool catalogue by the same functions that build kairon --help, on every release. The page and your terminal cannot disagree, because they are the same code.

kairon campaign

kairon campaign activate

Activate a campaign — starts sending to real people

Full description

THIS STARTS SENDING MESSAGES TO REAL PEOPLE ON LINKEDIN, and a message already sent cannot be un-sent.

Flags: campaign activate
FlagTypeWhat it is for
--startNowbooleanDefault true: enroll today's first batch immediately instead of waiting for the next send window to open.
--idrequiredstringThe campaign id, as `campaign_list` or `campaign_create` returned it.

kairon campaign copy

Who still needs a per-lead line

Full description

Per-lead slot coverage.

Flags: campaign copy
FlagTypeWhat it is for
--idrequiredstring
--missingOnlybooleanTrue returns only the leads still MISSING copy — the "what is left to write" read. A lead is not enrolled until every slot is filled, so these are the people the campaign is silently holding back.
--cursorstring
--limitinteger

kairon campaign create

Create a draft campaign

Full description

Create a draft campaign: a name and the sending seat (channelAccountId, a connected LinkedIn account in this org — seat_list returns the ones you can pick), and the ICP to gate enrollment on (icpId).

Flags: campaign create
FlagTypeWhat it is for
--namerequiredstringThe campaign's name, for the operator to recognise it in a list. Not seen by any lead.
--channelAccountIdrequiredstringThe sending seat — a connected LinkedIn account in this org. Every send comes FROM it. Get the id from `seat_list` — the seats that can send right now. (`identity_get`'s `seatId` names your own account even when it is not sendable, so a campaign built on it is created, publishes fine, and is then refused by `campaign_activate` with `sender_not_connected`.)
--icpIdstringThe ICP to gate enrollment on. There is no default and nothing is selected for you: omitting this creates an UNGATED campaign that contacts everyone on its lists. ASK the operator before calling, every time — never decide it for them and never leave it unasked. Show them their ICPs (`icp_list`) and say in one line what each answer means: gated, and only people who qualify for that ICP are contacted while everyone else on their lists is skipped and never messaged; ungated, and everyone on their lists is contacted. "No gate" is a fine answer, but it has to be one they gave.

kairon campaign get

One campaign in full — config, audience and both graphs

Full description

Read ONE campaign completely enough to REBUILD it.

Flags: campaign get
FlagTypeWhat it is for
--idrequiredstringThe campaign id, as `campaign_list` or `campaign_create` returned it.

kairon campaign lead-start

Start one queued person today, ahead of the pacing

Full description

Enroll one person from the campaign's audience now, instead of waiting for tomorrow's batch.

Flags: campaign lead-start
FlagTypeWhat it is for
--idrequiredstringThe campaign id, as `campaign_list` or `campaign_create` returned it.
--leadIdrequiredstringThe lead to start, as `campaign_runs` or `list_members` returned its id.

kairon campaign list

List the org’s campaigns

Full description

List this org's campaigns, keyset-paginated — { q?, status?, sort?, direction?, cursor?, limit? }.

Flags: campaign list
FlagTypeWhat it is for
--qstringCase-insensitive substring of the campaign's name.
--statusjsonjsonReturn only campaigns in these statuses — one, or several as a comma-separated list (e.g. `draft,active,paused`). Omit for every status, including the finished and archived ones the app hides by default.
--initiativejsonjsonReturn only campaigns serving this initiative. The literal `none` returns the campaigns serving no initiative yet — the candidates an initiative can attach (PRD-79 IN8), since a campaign serves at most one.
--cursorstringOpaque keyset token from the previous page's `nextCursor`. Omit for the first page.
--limitintegerCampaigns per page, 1-100.
--sortname|status|sender|updatedOrder the page by this column instead of the default one. The default puts the campaigns that are running first — active, then draft, then paused, then finished, then archived — and the most recent first inside each of those.
--directionasc|descWhich way `sort` runs. Omit it to get the column read the way the app reads it: names and senders A→Z, statuses running-first, dates newest-first.

kairon campaign pause

Pause an active campaign

Full description

Pause an active campaign.

Flags: campaign pause
FlagTypeWhat it is for
--idrequiredstringThe campaign id, as `campaign_list` or `campaign_create` returned it.

kairon campaign preview

Read what a step would actually send, before anyone gets it

Full description

READ THE MESSAGE BEFORE YOU ACTIVATE.

Flags: campaign preview
FlagTypeWhat it is for
--idrequiredstringThe campaign id, as `campaign_list` or `campaign_create` returned it.
--nodeKeyrequiredstringWhich step to preview — the `key` of a node in the stored graph, as `campaign_get` returned it. Must be a step that writes something: `send_invite`, `send_message` or `comment_on_post`. Any other type is refused, because it has no message to show you.
--leadIdsjson arrayjsonWhich real leads to draft for, at most 3. Each must already sit in this campaign's bound audience; one that does not comes back as `lead_not_in_audience` rather than failing the call. OMIT IT and the first few of the audience are sampled for you, which is what you want unless you are checking one named person.
--graphpublished|draftWhich stored graph to read the step from. Defaults to `published` — what the engine is actually sending — and falls back to `draft` when nothing has been published yet. Ask for `draft` to check an edit you have not published. A `{{@slot}}` in a draft-graph step previews UNRESOLVED when nobody has written that line yet, which is every slot before the first publish, since per-lead copy is written against the published graph; a slot whose name already carries copy from an earlier publish renders normally here.

kairon campaign publish

Validate and publish the campaign's draft graph

Full description

Validate and publish the campaign's draft graph.

Flags: campaign publish
FlagTypeWhat it is for
--idrequiredstringThe campaign id, as `campaign_list` or `campaign_create` returned it.
--migrateInFlightbooleanDefault false: publishing pins the new version for FUTURE enrollments only, and runs already in flight finish on the version they started. True repoints them onto the new version by node `key` — a run parked on a step the new version removed is CANCELED, not migrated.

kairon campaign review-feed

The review desk — what is about to fire, and what is waiting on you

Full description

The operator's Tasks page, as one read: every send booked to fire (autopilot), every manual run waiting for approval (review), every AI draft that errored (failed), and everything already acted on today (done), plus each seat's daily budge…

Flags: campaign review-feed
FlagTypeWhat it is for
--kindautopilot|review|failed|doneWhich slice: `autopilot` (booked, about to fire), `review` (waiting for approval on a manual campaign), `failed` (the AI draft errored), `done` (already acted today). Omit for all four.
--campaignIdstringOnly rows belonging to this campaign — applied to the desk WINDOW, not to the campaign. The desk holds a bounded slice of the org, so a campaign whose rows fall outside it comes back empty; `windowCapped: true` is how you tell that apart from "nothing is queued". For an exhaustive per-campaign read use `campaign_runs`, which pages with a cursor.
--limitintegerRows PER KIND, 1-200, default 25 — so an unfiltered call can return up to FOUR times this. Spent per kind on purpose: one busy kind must not push the others off the page, and a `review` row is reachable from no other index. Pass `kind` to spend the whole budget on one. Raise it only when you actually need the long tail: every row carries the draft it will send.

kairon campaign run-approve

Approve a manual campaign’s parked send — it goes out

Full description

THIS SENDS THE MESSAGE.

Flags: campaign run-approve
FlagTypeWhat it is for
--runIdrequiredstringOne run's id — starts with `srun_`, as `campaign_runs` or `campaign_review_feed` returned it. A run is one enrolled lead. NOT a feed row's `id` or `nodeRunId`.

kairon campaign run-cancel

End these people’s sequences — nothing further is sent to them

Full description

Stop up to 100 enrolled leads: each run ends, anything drafted or booked for them is dropped, and their parked workflow is torn down so no timer fires later.

Flags: campaign run-cancel
FlagTypeWhat it is for
--runIdsrequiredjson arrayjsonThe runs to end — each starts with `srun_`, as `campaign_runs` or `campaign_review_feed` returned them. NOT a feed row's `id` or `nodeRunId`: on an `autopilot` row those name the SEND, and a batch that accepted them would report a retryable failure for an id that can never succeed.

kairon campaign run-get

One sequence run, step by step

Full description

Read ONE sequence run in full, by an id from campaign_runs.

Flags: campaign run-get
FlagTypeWhat it is for
--runIdrequiredstringOne run's id, as `campaign_runs` returned it — a run is one enrolled lead.

kairon campaign run-skip-wait

Collapse the delay this run is waiting on

Full description

Cut short the delay step a run is parked on so the sequence carries straight on to its next step.

Flags: campaign run-skip-wait
FlagTypeWhat it is for
--runIdrequiredstringOne run's id — starts with `srun_`, as `campaign_runs` or `campaign_review_feed` returned it. A run is one enrolled lead. NOT a feed row's `id` or `nodeRunId`.

kairon campaign runs

The campaign's sequence runs

Full description

Read the campaign's sequence runs — one per enrolled lead — keyset-paginated; page with the returned cursor.

Flags: campaign runs
FlagTypeWhat it is for
--bucketwaiting|didnt_accept|didnt_answer|replied|skipped|error|interestedFilter to one activity bucket: `waiting`, `didnt_accept`, `didnt_answer`, `replied`, `interested` (a subset of `replied`), `skipped` (an ICP / enrollment-filter verdict or an operator stop), or `error`. Omit for every run.
--stagejsonjsonActivity states to include; several read as "any of these".
--nodeKeystringOnly runs that reached this step, by the node `key` the graph gave it — including runs that have since moved past it or finished. Pair it with `dateFrom`/`dateTo` to ask who completed THIS step on those days, which is the only way to tell a first message apart from a follow-up in a sequence with more than one message step.
--qstringFind a person by name, headline, company or LinkedIn handle.
--dateFromstringEarliest day to include, inclusive (YYYY-MM-DD). Dates each run by its LAST event — unless `nodeKey` is also set, which dates it by that step instead.
--dateTostringLatest day to include, inclusive (YYYY-MM-DD). Dates each run by its LAST event — unless `nodeKey` is also set, which dates it by that step instead.
--listIdjsonjsonOnly people enrolled from these source lists.
--cursorstring
--limitinteger
--idrequiredstring
--includeQualificationbooleanAttach each run's stored qualification scorecard (the same body `qualification_get` returns for its lead). Off by default because it is BIG — every criterion with its rationale and evidence — so pair it with a small `limit` (10-25) rather than a full page.

kairon campaign send-edit

Rewrite a booked send before it fires

Full description

Replace the words a drafted or scheduled send will fire with — the edited text is exactly what goes out, never re-written afterwards.

Flags: campaign send-edit
FlagTypeWhat it is for
--nodeRunIdrequiredstringThe booked send — starts with `nrun_`, as `campaign_review_feed` returned its `nodeRunId`. NOT the run id. Null on a feed row means nothing is drafted yet, so there is nothing to act on.
--bodyrequiredstringThe exact text that will be sent — this REPLACES the draft, it is not an instruction to rewrite it. Empty is allowed only on a connection request, which then fires with no note. On a MESSAGE: To send this as SEVERAL messages rather than one, separate them with a line containing only `---`. Each part arrives as its own LinkedIn message a couple of seconds later, in the same conversation — what a person does when they type a short opener and then the reason they wrote. The whole thing still counts as ONE send against the daily limit. This is the same thing the "Add bubble" button does in the campaign editor. On every other send type a `---` line is sent as written.
--subjectstringAn InMail subject, and only that: refused on every other send type. It is the whole of what the recipient sees before deciding to open, and a credit is spent either way.

kairon campaign send-now

Fire this person’s next send immediately

Full description

THIS SENDS THE MESSAGE, sooner than anything else would have: it fires this run's next booked send NOW, outside the send window and the usual pacing.

Flags: campaign send-now
FlagTypeWhat it is for
--runIdrequiredstringOne run's id — starts with `srun_`, as `campaign_runs` or `campaign_review_feed` returned it. A run is one enrolled lead. NOT a feed row's `id` or `nodeRunId`.

kairon campaign set-graph

Replace the campaign's draft sequence graph

Full description

Replace the campaign's editable DRAFT sequence graph IN FULL — this overwrites, it is not a patch.

Flags: campaign set-graph
FlagTypeWhat it is for
--idrequiredstringThe campaign id, as `campaign_list` or `campaign_create` returned it.
--graph.nodesrequiredjson arrayjsonEvery step in the sequence. Layout is automatic — send no coordinates.
--graph.edgesrequiredjson arrayjsonHow one step's outcome routes to the next — these are fixed rules, and a violation is refused. A node emits the outcomes in [brackets]; an edge carries exactly one of them, to one of the types after the arrow. A `*` outcome MUST route onward; any other may end the flow. A wait_connection_accepted needs a send_invite SOMEWHERE upstream — not necessarily right before it. Put a second one further down a `timeout` branch to re-ask whether they have accepted yet; two waits back to back are refused. A wait that leads to another wait on its `timeout` branch MUST set withdrawOnTimeout:false — the default takes the invite back, leaving the later wait nothing that can be accepted. send_invite [sent] → wait_connection_accepted wait_connection_accepted [accepted*, timeout] → delay, send_message, like_last_post, visit_profile, follow_profile, remove_relation, comment_on_post, send_inmail, send_email, audience_split, wait_reply, agentic_reply delay [default*] → send_invite, wait_connection_accepted, send_message, like_last_post, visit_profile, follow_profile, remove_relation, comment_on_post, send_inmail, send_email, audience_split, wait_reply, agentic_reply send_message [sent] → wait_connection_accepted, delay, like_last_post, visit_profile, follow_profile, remove_relation, comment_on_post, send_email, audience_split, wait_reply, agentic_reply like_last_post [done] → send_invite, wait_connection_accepted, delay, send_message, visit_profile, follow_profile, remove_relation, comment_on_post, send_inmail, send_email, audience_split, wait_reply, agentic_reply visit_profile [done] → send_invite, wait_connection_accepted, delay, send_message, like_last_post, follow_profile, remove_relation, comment_on_post, send_inmail, send_email, audience_split, wait_reply, agentic_reply follow_profile [done] → send_invite, wait_connection_accepted, delay, send_message, like_last_post, visit_profile, remove_relation, comment_on_post, send_inmail, send_email, audience_split, wait_reply, agentic_reply remove_relation [done] → send_invite, wait_connection_accepted, delay, like_last_post, visit_profile, follow_profile, comment_on_post, send_inmail, send_email, audience_split, wait_reply comment_on_post [commented] → send_invite, wait_connection_accepted, delay, send_message, like_last_post, visit_profile, follow_profile, remove_relation, send_inmail, send_email, audience_split, wait_reply, agentic_reply send_inmail [sent] → wait_connection_accepted, delay, like_last_post, visit_profile, follow_profile, remove_relation, comment_on_post, send_email, audience_split, wait_reply, agentic_reply send_email [sent] → send_invite, wait_connection_accepted, delay, send_message, like_last_post, visit_profile, follow_profile, remove_relation, comment_on_post, send_inmail, audience_split, wait_reply, agentic_reply audience_split [branch_a*, branch_b*] → send_invite, wait_connection_accepted, delay, send_message, like_last_post, visit_profile, follow_profile, remove_relation, comment_on_post, send_inmail, send_email, audience_split, wait_reply, agentic_reply wait_reply [replied, timeout] → send_invite, wait_connection_accepted, delay, send_message, like_last_post, visit_profile, follow_profile, remove_relation, comment_on_post, send_inmail, send_email, audience_split, agentic_reply agentic_reply [done, no_reply] → wait_connection_accepted, delay, like_last_post, visit_profile, follow_profile, remove_relation, comment_on_post, send_email, audience_split PATH RULE (the rows cannot show this): after a wait's `timeout` outcome the lead is NOT connected, so NO send_message may follow it — a `delay` in between changes nothing. It stays banned down that branch until something says otherwise: a later gate's `accepted`, or a fresh accepted invite. Instead: nudge them with like_last_post / visit_profile / follow_profile / comment_on_post / send_inmail / send_email / audience_split / wait_reply / agentic_reply; re-ask with another wait_connection_accepted (the earlier wait then needs withdrawOnTimeout:false) and send off ITS `accepted`; send another invite and message off the new `accepted`. PATH RULE (the rows cannot show this): audience_split is SEE-THROUGH. It takes no time and sends nothing, so what may follow it is decided by the step BEFORE it, not by its own row above. A step you may not put directly after X you also may not put after a split that follows X.
--graph.startNodeKeyrequiredjsonjsonThe `key` of the step each lead begins on. Exactly one start: `null` lets it be inferred when only one node has no inbound edge, and publish fails with `no_start` or `multiple_starts` otherwise.

kairon campaign stats

How the campaign is doing

Full description

How a campaign is performing, in one read — and the read before campaign_activate.

Flags: campaign stats
FlagTypeWhat it is for
--idrequiredstringThe campaign id, as `campaign_list` or `campaign_create` returned it.
--daysjsonjsonWindow for the daily activity series, 7 or 30, in the campaign's own local days. OMIT IT and the series is left out entirely rather than defaulted — do not pay to read one you were not asked for.

kairon campaign update

Edit the campaign's configuration and audience

Full description

Edit a campaign's configuration — any subset of its fields; omitted ones are unchanged.

Flags: campaign update
FlagTypeWhat it is for
--namestringThe campaign's name, for the operator to recognise it. Not seen by any lead.
--sendWindow.daysjson arrayjsonWeekdays sends may fire, 0 = Sunday to 6 = Saturday. At least one, no duplicates.
--sendWindow.startHourintegerFirst hour sends may fire, 0-23, in `timeZone`. Must be before `endHour`.
--sendWindow.endHourintegerLast hour sends may fire, 0-23, in `timeZone`. Must be after `startHour`.
--sendWindow.timeZonestringIANA zone the hours are read in, e.g. `America/Argentina/Buenos_Aires`. ASK the operator rather than assuming: a valid zone that is not theirs is accepted silently and sends at the wrong hour every day, with nothing to flag it.
--autonomymanual|autopilot`autopilot` (the default) fires each send on schedule. `manual` parks EVERY send for the operator to approve first, so a manual campaign looks active and sends nothing until someone works the queue.
--allowsReEntrybooleanMay a person enter this campaign more than once? `false` (the default) means someone who finished it never comes back — the rule every campaign has always had. `true` lets them re-enter later, which is what a nurture campaign is for. It never permits two messages at once: one live run per person, always, whatever this says.
--dailyEnrollmentLimitintegerHow many leads may enroll per day, 1-200 (default 25). Caps the START of new runs, not sends — the org's own per-seat limits still apply on top.
--priorityjsonjsonWhich campaign this LinkedIn account serves first when two of them are due on the same day. 0 is highest. Lower sorts first, matching every other priority in Kairon. `null` clears it — no priority, served after Low; omitted leaves it unchanged. It is an ORDER, never an allowance: it does not change how many leads any campaign enrols (`dailyEnrollmentLimit` does that), and it never moves a send that is already scheduled.
--enrollmentFilter.bucketsjson arrayjsonWhich contact-history buckets may enroll: `new` (never contacted), `unanswered` (we messaged, no reply), `in_conversation` (they replied). Unique, at least one. All three means enroll everyone — the default. Anyone outside the selected buckets is skipped, not queued, so a narrow set is why a large audience enrolls almost nobody.
--enrollmentFilter.scopeseat|orgWhose prior-contact history is read: `seat` (this campaign's sending seat) or `org` (every LinkedIn seat). Copying a `seat` filter onto a DIFFERENT seat changes what it means — the new seat's history is empty, so people the original filter excluded now enroll.
--icpIdjsonjsonThe ICP that gates enrollment. `null` clears it (enroll everyone); omitted leaves it unchanged. Must be an ICP in this org. Leads that fail it are marked Skipped, never messaged.
--stopOnReplybooleanDefault `true` — a reply halts that person's run so nothing follows a real conversation. `false` keeps the sequence going after they answer; set it only when the operator asked.
--connectionDegreeany|only_first|exclude_firstWhether the sender's existing LinkedIn connection matters. `any` (the default) ignores it. `only_first` enrolls ONLY people the sender is already connected to. `exclude_first` enrolls only people they are NOT connected to — the usual choice for cold outreach that opens with an invite. Anything but `any` costs one extra LinkedIn read per candidate, and skips the people it excludes rather than queueing them.
--stopOnCompanyReplyoff|any_reply|interestedWhether one person answering stops their colleagues. `off` (the default) works every lead on their own merits. `any_reply` stops the rest of a company once anyone there answers THIS campaign. `interested` stops it only when the answer was an interested one, so a colleague's "no thanks" leaves the others working. Colleagues are matched on their LinkedIn employer, so someone whose employer Kairon has never seen is never stopped by it. It stops people waiting to enter AND people already inside — but never someone who has written to you, whose conversation always continues.
--idrequiredstringThe campaign id, as `campaign_list` or `campaign_create` returned it.
--leadListIdsjson arrayjsonLead lists to bind, ADDITIVELY. A list already bound stays bound, and a bound list you leave out is NOT unbound — omission cannot unbind, because that would cancel every in-flight run drawn from the list.
--initiativeIdjsonjsonThe initiative this campaign serves. `null` detaches it; omitted leaves it unchanged. A campaign serves at most ONE bet, so a different id MOVES it. Detaching changes nothing else — a campaign that outlives its bet keeps sending.

kairon campaign version-restore

Bring an earlier sequence graph back as the draft

Full description

Copy an earlier published version back into the campaign's editable DRAFT, steps and routing and layout intact — the way to undo an edit that replaced copy someone wrote.

Flags: campaign version-restore
FlagTypeWhat it is for
--idrequiredstringThe campaign id, as `campaign_list` or `campaign_create` returned it.
--versionNumberrequiredintegerThe version to bring back, as `campaign_versions` listed it.

kairon campaign versions

Read the campaign's earlier sequence graphs

Full description

Every version this campaign has published, newest first, plus its unpublished draft — with a line per version saying what it changed (steps added/removed/edited, routing).

Flags: campaign versions
FlagTypeWhat it is for
--idrequiredstringThe campaign id, as `campaign_list` or `campaign_create` returned it.
--versionNumberintegerOmit for the list of versions. Give one to get THAT version’s full graph, in exactly the shape `campaign_set_graph` accepts.

kairon chat

kairon chat list

List conversations

Full description

List conversations, newest activity first, across every channel that holds them.

Flags: chat list
FlagTypeWhat it is for
--seatIdstringOne teammate's seat id, from `seat_list`. Defaults to your own.
--scopemine|org'mine' (default) is your own seat; 'org' is every seat in the organization.
--unreadbooleanTrue returns only chats with unread messages.
--answeredyes|no'no' returns chats waiting on a reply from you (they spoke last); 'yes' returns the ones you already answered. Chats whose history has not been loaded yet match neither.
--leadsOnlybooleanOnly chats already linked to a lead.
--needsHumanbooleanOnly chats where Kairon handed the conversation back and is waiting on you.
--qstringCase-insensitive match on the counterpart's NAME.
--cursorstring
--limitinteger

kairon chat messages

Read a conversation

Full description

Read one conversation's messages, newest first, by the chatId from chat_list — LinkedIn or email.

Flags: chat messages
FlagTypeWhat it is for
--chatIdrequiredstring
--cursorstring
--limitinteger

kairon checkpoint

kairon checkpoint delete

Withdraw a checkpoint

Full description

Remove one checkpoint from a bet’s history.

Flags: checkpoint delete
FlagTypeWhat it is for
--initiativeIdrequiredstringThe initiative the checkpoint belongs to.
--checkpointIdrequiredstringThe checkpoint id (`ckpt_…`), as `initiative_get` returned it in the history.

kairon checkpoint edit

Correct a checkpoint

Full description

Fix what one checkpoint says, or the verdict it froze — for a typo or a verdict typed by mistake.

Flags: checkpoint edit
FlagTypeWhat it is for
--bodystringWhat happened and the ONE thing to do next, in markdown. Say the numbers you read and how they compare to this org’s own baseline, and whether the last checkpoint’s recommendation was actually done. To put a teammate’s name on a next step, mention them: `[@Ana](#[email protected])` — a normal markdown link whose destination is `#kairon-` plus their user id, email, or name (spaces as `+`). Whichever you use is stored as their id, and a name nobody here matches, or one two teammates share, is refused rather than saved as prose. Read user ids and names off `seat_list`.
--healthon_track|at_risk|off_trackThe verdict, frozen at publish: `on_track`, `at_risk` or `off_track`. Judge VOLUME before rates — a low number of sends is not a bad result, it is not a result yet.
--initiativeIdrequiredstringThe initiative the checkpoint belongs to.
--checkpointIdrequiredstringThe checkpoint id (`ckpt_…`), as `initiative_get` returned it in the history.

kairon checkpoint publish

Publish a checkpoint on a bet

Full description

Record a dated verdict on an initiative: what happened, the one thing to do next, and a health of on_track, at_risk or off_track.

Flags: checkpoint publish
FlagTypeWhat it is for
--bodyrequiredstringWhat happened and the ONE thing to do next, in markdown. Say the numbers you read and how they compare to this org’s own baseline, and whether the last checkpoint’s recommendation was actually done. To put a teammate’s name on a next step, mention them: `[@Ana](#[email protected])` — a normal markdown link whose destination is `#kairon-` plus their user id, email, or name (spaces as `+`). Whichever you use is stored as their id, and a name nobody here matches, or one two teammates share, is refused rather than saved as prose. Read user ids and names off `seat_list`.
--healthrequiredon_track|at_risk|off_trackThe verdict, frozen at publish: `on_track`, `at_risk` or `off_track`. Judge VOLUME before rates — a low number of sends is not a bad result, it is not a result yet.
--initiativeIdrequiredstringThe initiative being judged (`init_…`).

kairon company

kairon company followers

List a company page’s followers

Full description

The people who follow a LinkedIn company page YOUR seat administers — the second opted-in audience after a newsletter, and often the bigger one.

Flags: company followers
FlagTypeWhat it is for
--targetrequiredjsonjsonThe company page whose followers to read — named by URL, universal name, or provider id. It must be a page this seat ADMINISTERS; LinkedIn shows followers to admins only.
--cursorstringContinue from a previous page. Omit for the first page.
--countintegerFollowers per page (default 50, the most LinkedIn allows for a page).
--intoListstringAppend this page’s followers to one of your lead lists, by name or id. Combine with `exhaust` to walk the whole audience into it.
--exhaustbooleanPage server-side until the results run out, instead of returning a cursor for you to send back. Requires `intoList`: every page is appended there and NO hits come back, only the totals — which is the point, since neither the hits nor the cursor then cross your context. Stops at `maxResults` and hands back a `cursor` to resume from. Each page still costs one metered action.
--maxResultsintegerHow many hits an `exhaust` run collects before it stops (default 500, max 2000). Ignored without `exhaust`. Raise it when you know the audience is large and you mean to spend the actions.

kairon company posts

A company page's recent posts

Full description

A page of a COMPANY page's own posts — what a competitor, a partner or a target account publishes under its own name, not what its people post.

Flags: company posts
FlagTypeWhat it is for
--targetrequiredjsonjsonThe company page whose posts to read — named by URL, universal name, or provider id.
--cursorstring

kairon config

kairon config get

Read the operator's own material

Full description

What the operator has told Kairon: their competitor and source URLs, and their organization's value proposition, lead magnet and learnings log.

Flags: config get
FlagTypeWhat it is for
--sectionsjson arrayjsonWhich sections to read. Omit for all five.

kairon config set

Save the operator's own material

Full description

Replace one or more sections of what the operator has told Kairon: competitorUrls, sourceUrls, valueProposition, leadMagnet, learnings.

Flags: config set
FlagTypeWhat it is for
--competitorUrlsjson arrayjsonReplaces every competitor URL. Each must be a LinkedIn URL.
--sourceUrlsjson arrayjsonReplaces every source URL. Any http(s) URL.
--valuePropositionstringReplaces the organization's value proposition. An empty string clears it.
--leadMagnetstringReplaces the organization's lead magnet — what it gives a prospect to earn a reply. An empty string clears it.
--learningsstringReplaces the organization's GTM learnings log WHOLE. To append an entry, read the log first and write back the joined text. An empty string clears it.

kairon doc

kairon doc get

Read one product doc

Full description

One product doc in full, as markdown, by a slug from doc_list.

Flags: doc get
FlagTypeWhat it is for
--slugrequiredstringWhich doc to read — a `slug` from `doc_list`, e.g. "campaigns".

kairon email

kairon email domain-buy

Buy the sending domain

Full description

Order a sending domain and its mailboxes.

Flags: email domain-buy
FlagTypeWhat it is for
--domainrequiredstringThe domain name to buy and send from, like `acme-outreach.com`. Only `.com` and `.org` can be registered, and a name carrying a well-known trademark is refused. Not the customer's real website — a sending domain is a separate name, so a problem with it never touches their main domain's reputation.
--mailboxesrequiredjson arrayjsonThe mailboxes to create on the domain — one to five, each a real person on the team, since a recipient who replies is replying to that name. More mailboxes means more sending capacity and more monthly cost.
--forwardingDomainstringThe customer's real website, where a visitor to the sending domain is sent — `acme.com`. Worth setting: a sending domain that resolves to nothing is exactly what a suspicious recipient checks. Any extension, not just .com/.org. Ask the operator; never guess it from their email address.
--agreedTotalrequirednumberThe `dueNow` from the quote the operator agreed to, in whole dollars. The order is re-priced against the vendor immediately before buying and refused if the number moved, so a stale quote can never charge a different amount than the one shown.

kairon email domain-check

Check a sending domain’s DNS records

Full description

Ask Instantly whether this domain's MX, SPF, DKIM and DMARC records are set up correctly.

Flags: email domain-check
FlagTypeWhat it is for
--domainrequiredstringA sending domain this workspace already has — read them from `email_setup_get`. Not any domain: this must be one Kairon sends from for this org.

kairon email domain-quote

What would this sending domain cost?

Full description

Price a sending domain and its mailboxes.

Flags: email domain-quote
FlagTypeWhat it is for
--domainrequiredstringThe domain name to buy and send from, like `acme-outreach.com`. Only `.com` and `.org` can be registered, and a name carrying a well-known trademark is refused. Not the customer's real website — a sending domain is a separate name, so a problem with it never touches their main domain's reputation.
--mailboxesrequiredjson arrayjsonThe mailboxes to create on the domain — one to five, each a real person on the team, since a recipient who replies is replying to that name. More mailboxes means more sending capacity and more monthly cost.
--forwardingDomainstringThe customer's real website, where a visitor to the sending domain is sent — `acme.com`. Worth setting: a sending domain that resolves to nothing is exactly what a suspicious recipient checks. Any extension, not just .com/.org. Ask the operator; never guess it from their email address.

kairon email mailbox-add

Add a mailbox the customer already owns

Full description

Add a mailbox the customer ALREADY owns — on their own domain, in their own Google Workspace or Microsoft account — so Kairon can send from it.

Flags: email mailbox-add
FlagTypeWhat it is for
--emailrequiredstringThe full address of a mailbox the customer already owns, like `[email protected]`.
--firstNamerequiredstringThe mailbox owner's first name — it appears in the From line of every send.
--lastNamerequiredstringThe mailbox owner's last name.
--providerrequiredgoogle|microsoft|customWho hosts the mailbox. `google` for Google Workspace or Gmail, `microsoft` for Microsoft 365 or Outlook — both fill in their own server settings. `custom` for anything else, and then all four host and port fields are required.
--passwordrequiredstringAn APP PASSWORD for the mailbox, not the account password. For Google the operator creates one in their Google account with 2-step verification on. Ask them for it directly and never invent one; it is passed straight to the sending provider and stored nowhere in Kairon.
--smtpHoststringOutgoing mail server, `custom` provider only (Google and Microsoft are known).
--smtpPortintegerOutgoing mail port, `custom` provider only — usually 465 or 587.
--imapHoststringIncoming mail server, `custom` provider only.
--imapPortintegerIncoming mail port, `custom` provider only — usually 993.

kairon email plan-request

Ask Kairon for email sending or enrichment credits

Full description

Ask Kairon to set this workspace up for email sending (plan), or to add enrichment credits (credits).

Flags: email plan-request
FlagTypeWhat it is for
--kindrequiredplan|credits`plan` to have Kairon set this workspace up for email sending at all; `credits` to add enrichment credits, which are what finds a prospect's email address. Ask for `plan` first — credits are useless without somewhere to send from.
--quantityintegerHow many credit packs are wanted. Ignored for `plan` — there is one workspace.

kairon email setup-get

Read the email sending setup

Full description

How this workspace sends email: whether it is on Kairon's sending plan, which sending domains it has and what each mailbox is doing (still being created, warming up, sending, or in trouble), how much of the plan is used, how many enrichmen…

Takes no flags.

kairon email warmup-start

Start warmup on a domain’s mailboxes

Full description

Start warming up every mailbox on this sending domain.

Flags: email warmup-start
FlagTypeWhat it is for
--domainrequiredstringA sending domain this workspace already has — read them from `email_setup_get`. Not any domain: this must be one Kairon sends from for this org.

kairon exclusion

kairon exclusion add

Forbid contact with people or companies

Full description

Forbid contact with 1..1000 people or companies.

Flags: exclusion add
FlagTypeWhat it is for
--facetrequiredpeople|companiesWhat these items are. `people` suppresses each named individual. `companies` suppresses the company itself AND everyone who works there.
--itemsrequiredjson arrayjsonThe entries to add, up to 1000. Each is judged on its own, so one invalid item never fails the rest.

kairon exclusion list

Read the organization's exclusion list

Full description

Who this organization must never contact — the org-wide suppression list, in two facets: people (named individuals) and companies (the company itself AND everyone who works there).

Flags: exclusion list
FlagTypeWhat it is for
--facetpeople|companiesRead one facet only. Omit to get both, which is what "is this person blocked?" needs — someone can be suppressed by their employer's entry rather than by their own.

kairon exclusion remove

Remove one entry from the exclusion list

Full description

Stop suppressing one person or one company, by the entry id exclusion_list returned (exper_… on people, excom_… on companies).

Flags: exclusion remove
FlagTypeWhat it is for
--facetrequiredpeople|companiesWhich facet the entry sits on. It decides the id prefix: `people` → `exper_…`, `companies` → `excom_…`.
--idrequiredstringThe entry id, exactly as `exclusion_list` returned it.

kairon feedback

kairon feedback report

Report a problem back to Kairon

Full description

Tell Kairon that something went wrong for this user.

Flags: feedback report
FlagTypeWhat it is for
--problemrequiredstringWhat the user was trying to do, and what went wrong, in your own words. Concrete beats polite: name the entity, the filter, the campaign, whatever they were actually working on.
--severityrequiredblocked|friction`blocked` — the user cannot get what they came for and there is no way around it. `friction` — they got there, but it cost extra turns, a workaround, or visible annoyance. When unsure, say `friction`; a wrong `blocked` spends someone's attention on the wrong hour.
--toolsjson arrayjsonThe Kairon tools involved, in the order you called them. Include a tool that returned no error but the wrong answer — that case is the reason this field is not just an error string.
--quotestringThe user's own words, when they explain the problem better than your summary. Their exact phrasing, no more than a sentence or two. Omit it rather than paraphrase — a paraphrase here is just your summary twice.

kairon filter

kairon filter resolve

Resolve a Sales Navigator filter

Full description

Resolve a human-readable Sales Navigator filter value (location, industry, function) to a LinkedIn id, echoing the canonical label so a wrong match is visible.

Flags: filter resolve
FlagTypeWhat it is for
--typerequiredlocation|industry|function|current_company|past_company|company_location|school|past_role|groups|persona|technologies|account_lists|lead_lists|postal_codeWhich Sales Navigator dimension the name belongs to.
--queryrequiredstringThe human-readable NAME to resolve, e.g. "San Francisco Bay Area". Never a LinkedIn id — resolving one is what this is for, and the canonical label comes back so a wrong match is visible.

kairon framework

kairon framework get

Read one go-to-market axiom

Full description

One axiom in full, as markdown, by a slug from framework_list.

Flags: framework get
FlagTypeWhat it is for
--slugrequiredstringWhich axiom to read — a `slug` from `framework_list`.

kairon framework list

List the go-to-market axioms

Full description

Kairon's go-to-market theory: the standalone truths every playbook rests on, each a slug, an area (belief-system, targeting, outreach, content, loop) and a title that says exactly what is inside.

Takes no flags.

kairon group

kairon group members

List a LinkedIn group’s members

Full description

The members of a LinkedIn group, from the group’s own member list — people who joined a community around a topic, which is a targeting fact no search filter can express.

Flags: group members
FlagTypeWhat it is for
--grouprequiredstringThe group, as its URL (https://www.linkedin.com/groups/<id>/) or its bare numeric id. Public groups need no membership and no Sales Navigator; a private group is readable only by a member seat.
--cursorstringContinue from a previous page. Omit for the first page.
--countintegerMembers per page (default 50, max 100). LinkedIn refuses anything higher.
--intoListstringAppend this page’s members to one of your lead lists, by name or id. Combine with `exhaust` to walk the group into it without any of it crossing your context.
--exhaustbooleanPage server-side until the results run out, instead of returning a cursor for you to send back. Requires `intoList`: every page is appended there and NO hits come back, only the totals — which is the point, since neither the hits nor the cursor then cross your context. Stops at `maxResults` and hands back a `cursor` to resume from. Each page still costs one metered action.
--maxResultsintegerHow many hits an `exhaust` run collects before it stops (default 500, max 2000). Ignored without `exhaust`. Raise it when you know the audience is large and you mean to spend the actions.

kairon icp

kairon icp create

Create an ICP

Full description

Create an ICP — a target thesis spanning the companies to reach and the people inside them — at version 1.

Flags: icp create
FlagTypeWhat it is for
--namerequiredstringA short, specific name — the segment, not the pitch.
--definition.descriptionstringThe targeting thesis in prose: who these companies and people are, the pains they carry, and the observable signals that say one needs this NOW. Not sent to Sales Navigator and not judged as a check — it is the reasoning the filters were derived from.
--definition.companySalesNavCriteriajsonjson
--definition.peopleSalesNavCriteriajsonjson
--definition.companyQualificationConditionsjson arrayjsonJudges the COMPANY. Rules: see `definition`.
--definition.peopleQualificationConditionsjson arrayjsonJudges the PERSON. Rules: see `definition`.

kairon icp get

Get an ICP

Full description

Read one ICP and its latest (active) version by icpId.

Flags: icp get
FlagTypeWhat it is for
--icpIdrequiredstring

kairon icp qualify

Record a qualification verdict against an ICP

Full description

Qualify one LinkedIn person or company against a saved ICP version and PERSIST the verdict: you judge the AI checks, Kairon judges the structured filters and owns the row.

Flags: icp qualify
FlagTypeWhat it is for
--targetrequiredjsonjsonWho this acts on — one LinkedIn person or company, named by URL, vanity slug, or provider id.
--kindprofile|companyOverride auto-detection. Required for a company addressed by universal name or numeric id.
--icpVersionIdrequiredstring
--resultsjson arrayjsonOne entry per criterion you judged, keyed by `criterionId` from the ICP version.

kairon icp update

Update an ICP

Full description

Revise an ICP: mints a new immutable version, which becomes the active one.

Flags: icp update
FlagTypeWhat it is for
--icpIdrequiredstring
--namerequiredstringA short, specific name — the segment, not the pitch.
--definition.descriptionstringThe targeting thesis in prose: who these companies and people are, the pains they carry, and the observable signals that say one needs this NOW. Not sent to Sales Navigator and not judged as a check — it is the reasoning the filters were derived from.
--definition.companySalesNavCriteriajsonjson
--definition.peopleSalesNavCriteriajsonjson
--definition.companyQualificationConditionsjson arrayjsonJudges the COMPANY. Rules: see `definition`.
--definition.peopleQualificationConditionsjson arrayjsonJudges the PERSON. Rules: see `definition`.

kairon icp version-restore

Bring an earlier ICP definition back

Full description

Bring an earlier version back by minting a NEW version equal to it, which becomes the one in use straight away — unlike the campaign equivalent, there is no draft to review first, so confirm with the operator before calling.

Flags: icp version-restore
FlagTypeWhat it is for
--icpIdrequiredstring
--versionrequiredintegerThe version to bring back, as `icp_versions` listed it.

kairon icp versions

Read an ICP's earlier definitions

Full description

Every version of this ICP, newest first, with the date and the check counts for each — and which one is in use.

Flags: icp versions
FlagTypeWhat it is for
--icpIdrequiredstring
--versionintegerOmit for the list of versions. Give one to get THAT version in full.

kairon identity

kairon identity get

Who you are acting as

Full description

Who this connection acts as: the user, their organization, the seat (seatId plus its state: connected, none, or locked — needs a subscription), linkedin, and capabilities.

Takes no flags.

kairon impersonate

kairon impersonate list

List who you can act as

Full description

The users you may act as, each with their organization and whether they have a LinkedIn account connected — you can only act as someone who does.

Flags: impersonate list
FlagTypeWhat it is for
--searchstringFilter by name, email address or organization name.

kairon impersonate start

Act as another user

Full description

Act as another user from now on: every later call runs as them, on their LinkedIn seat and their organization.

Flags: impersonate start
FlagTypeWhat it is for
--userrequiredstringThe email address or user id to act as.

kairon initiative

kairon initiative delete

Withdraw a go-to-market bet

Full description

Remove one initiative from the board.

Flags: initiative delete
FlagTypeWhat it is for
--initiativeIdrequiredstringThe initiative to remove, by id (`init_…`).

kairon initiative get

Read one go-to-market bet

Full description

One initiative whole: the thinking in its body, the campaigns serving it as the campaigns list knows them, and every checkpoint newest-first with the verdict each one froze.

Flags: initiative get
FlagTypeWhat it is for
--initiativeIdrequiredstringThe initiative to read, by id (`init_…`).

kairon initiative list

List the go-to-market bets

Full description

Every initiative in the organization: what is being bet on, its status, the health of its latest checkpoint and how many days old that verdict is, plus the summed funnel of the campaigns serving it.

Flags: initiative list
FlagTypeWhat it is for
--statusdraft|active|completed|canceledReturn only initiatives in this state. Omit for all of them, including completed and canceled ones.

kairon initiative set

Create or edit a go-to-market bet

Full description

Write down a bet: a name, the thinking in markdown, and who is accountable for it.

Flags: initiative set
FlagTypeWhat it is for
--namestringShort, scannable name for the bet — "Expansión Centroamérica". REQUIRED when creating (no `initiativeId`); optional when editing. A create without one is refused rather than defaulted.
--bodyjsonjsonThe thinking, in markdown: what is being bet on, why, what would prove it worked, and what has been learned. Free-form on purpose — write what an operator would want to reread in three weeks. Put a teammate on a step by mentioning them: `[@Ana](#[email protected])`, a normal markdown link whose destination is `#kairon-` plus their user id, email, or name (spaces as `+`). It is stored as their id; a name nobody here matches, or one two teammates share, is refused. `seat_list` names them.
--statusdraft|active|completed|canceled`draft` while it is being shaped, `active` once it is being worked.
--priorityjsonjson0 is highest. Lower sorts first, matching every other priority in Kairon.
--startDatejsonjson`YYYY-MM-DD`, this century. A bet starts on a date, not at a moment.
--endDatejsonjson`YYYY-MM-DD`, this century. When the bet should have paid off, or be called. Must not be before `startDate`.
--initiativeIdstringOmit to create a new initiative. Pass an id (`init_…`) to edit that one. Call `initiative_list` FIRST and reuse the id of any bet that already covers this — omitting it a second time creates a duplicate, it does not update.
--ownerjsonjsonWho is accountable for this bet — one org member, by `email` (what you usually have) or `userId`. `null` leaves it unassigned; omit to leave it as it is.

kairon inmail

kairon inmail send

Send an InMail

Full description

Send an InMail (optional subject) to the person target addresses.

Flags: inmail send
FlagTypeWhat it is for
--targetrequiredjsonjsonWho this acts on — one LinkedIn person or company, named by URL, vanity slug, or provider id.
--subjectstringThe InMail subject line — the one thing that decides whether it is opened.
--textrequiredstringThe InMail body, exactly as it will be sent. Nothing rewrites it.
--attachmentsjson arrayjson

kairon insights

kairon insights report

How the whole account is performing

Full description

The org-wide outreach funnel over a date window — invites sent → accepted → messages → replied → interested — with the daily activity series and a per-seat breakdown, each seat carrying its campaigns and their acceptance, reply and interes…

Flags: insights report
FlagTypeWhat it is for
--fromrequiredstringFirst day of the window, `YYYY-MM-DD`, inclusive.
--torequiredstringLast day of the window, `YYYY-MM-DD`, inclusive. Must be on or after `from`, and the window must not exceed 366 days.
--userIdstringNarrow to one seat owner. Omit for every seat in the organization.

kairon invitations

kairon invitations received

Pending connection requests

Full description

The connection requests waiting in your LinkedIn inbox — people who asked to connect with YOU and have not been answered.

Flags: invitations received
FlagTypeWhat it is for
--cursorstringContinue from a previous page. Omit for the first page.
--countintegerInvitations per page (default 50, max 100).
--intoListstringAppend the people who invited you to one of your lead lists, by name or id. Combine with `exhaust` to walk every pending request into it.
--exhaustbooleanPage server-side until the results run out, instead of returning a cursor for you to send back. Requires `intoList`: every page is appended there and NO hits come back, only the totals — which is the point, since neither the hits nor the cursor then cross your context. Stops at `maxResults` and hands back a `cursor` to resume from. Each page still costs one metered action.
--maxResultsintegerHow many hits an `exhaust` run collects before it stops (default 500, max 2000). Ignored without `exhaust`. Raise it when you know the audience is large and you mean to spend the actions.

kairon invite

kairon invite send

Send a connection invitation

Full description

Send a LinkedIn connection invitation (optional note) through the caller's account.

Flags: invite send
FlagTypeWhat it is for
--targetrequiredjsonjsonWho this acts on — one LinkedIn person or company, named by URL, vanity slug, or provider id.
--notestringThe connection-request note, up to 300 characters. Omit it entirely for a note-less invite, which is valid and often accepts better.

kairon job

kairon linkedin

kairon linkedin fetch

Fetch a LinkedIn profile or company

Full description

Fetch a LinkedIn person OR company through the caller's connected account — whichever the target addresses ({ type, value }: url, public_identifier, or provider_id).

Flags: linkedin fetch
FlagTypeWhat it is for
--targetrequiredjsonjsonWho this acts on — one LinkedIn person or company, named by URL, vanity slug, or provider id.
--kindprofile|companyOverride auto-detection. Required for a company addressed by universal name or numeric id.
--refreshbooleanTrue bypasses the cache and forces a live read from LinkedIn. Slower, and it spends a lookup against your headroom.
--withRecommendationsbooleanTrue also returns who recommended this person (and who they recommended), with each recommender identified. Warm-intro paths. Always spends a live lookup — never served from cache.

kairon list

kairon list add

Add members to a list

Full description

Add members to a list ({ asset, id, items }, up to 500).

Flags: list add
FlagTypeWhat it is for
--assetrequiredleads|companiesWhich kind of list: 'leads' (people) or 'companies'.
--idrequiredstring
--itemsrequiredjson arrayjsonThe rows to add, each naming its subject by ONE of `memberId`, `url`, `providerId`, or `snapshot`.

kairon list create

Create a list

Full description

Create a named list — the campaign targeting unit.

Flags: list create
FlagTypeWhat it is for
--assetrequiredleads|companiesWhich kind of list: 'leads' (people) or 'companies'.
--namerequiredstringThe list's name, for the operator to recognise it.

kairon list delete

Delete a list

Full description

Soft-delete a list by asset + id: it disappears from list_list with its memberships, the member leads/companies are untouched (they stay in the org pool, in their other lists, with their qualification state intact), and the name is immedia…

Flags: list delete
FlagTypeWhat it is for
--assetrequiredleads|companiesWhich kind of list: 'leads' (people) or 'companies'.
--idrequiredstring

kairon list get

Get one list

Full description

One list by asset + id: its name, live member count, created date, and its ordered columns (the list's own custom columns — free-text fields you attach to its members).

Flags: list get
FlagTypeWhat it is for
--assetrequiredleads|companiesWhich kind of list: 'leads' (people) or 'companies'.
--idrequiredstring

kairon list list

List lists

Full description

Read this org's lists of one asset, keyset-paginated, newest first, each with its live member count.

Flags: list list
FlagTypeWhat it is for
--assetrequiredleads|companiesWhich kind of list: 'leads' (people) or 'companies'.
--qstringCase-insensitive substring of the list's name.
--cursorstring
--limitinteger

kairon list members

Read a list's members

Full description

One keyset page of a list's members.

Flags: list members
FlagTypeWhat it is for
--assetrequiredleads|companiesWhich kind of list: 'leads' (people) or 'companies'.
--idrequiredstring
--qstringCase-insensitive substring over the member's display fields.
--cursorstring
--limitinteger
--tagsjson arrayjsonKeep only members carrying EVERY tag named — an AND, not an OR. Matched case-insensitively; a name no tag has yields an empty page rather than an error.
--sortperson|title|company|email|industry|qualification|owner|added|sourceWhich column to order by. Leads: person, title, company, qualification, owner, added, source. Companies: company, industry, qualification, owner, added. Naming a field the asset does not have is refused. Omit for the default, newest-added first.
--directionasc|descOrder direction; read as `asc` once a `sort` field is named.
--qualificationStatusjsonjsonKeep members whose ICP verdict is ANY OF these: qualified, disqualified, unevaluated. `unevaluated` means nobody has judged them — never that they failed.
--ownerIdjsonjsonKeep members owned by ANY OF these user ids. Ownership rides the entity, not the list.
--tagIdjsonjsonKeep members carrying ANY OF these tag ids — an OR, and the counterpart to `tags` above, which is an AND over NAMES.
--addedAfterstringKeep members added to THIS list at or after this ISO timestamp.
--addedBeforestringKeep members added to THIS list strictly before this ISO timestamp.
--hydratedbooleantrue keeps members whose LinkedIn facts have been loaded; false keeps the ones nobody has fetched yet (they have no size or HQ to filter on).
--columnjson arrayjsonKeep members matching EVERY predicate over this list's custom columns. A name the list does not have is refused, so a typo cannot read as 'no matches'.
--includeQualificationbooleanAttach each member's stored qualification scorecard — the same body `qualification_get` returns, so a half-rejected list explains itself in ONE call instead of one per member. This is how you answer 'why were these disqualified'. Works on both assets. Off by default because it is BIG — every criterion with its rationale and evidence — so pair it with a small `limit` (10-25) rather than a full page.
--signalKeyjsonjsonLeads only. Keep members that arrived from ANY OF these signals.
--crmStagejson arrayjsonLeads only. Keep members standing at ANY OF these pipeline stages.
--employeeMinintegerCompanies only. Keep companies with AT LEAST this many employees.
--employeeMaxintegerCompanies only. Keep companies with AT MOST this many employees.
--industryjson arrayjsonCompanies only. Keep companies carrying ANY OF these industries, matched case-insensitively against the whole industry list, not just the one shown as `subtitle`.
--hqCountryjson arrayjsonCompanies only. Keep companies whose HQ is in ANY OF these ISO-2 country codes.

kairon list qualify

Qualify a list against an ICP

Full description

Judge a list's members against an ICP and return the run that started.

Flags: list qualify
FlagTypeWhat it is for
--assetrequiredleads|companiesWhich kind of list: 'leads' (people) or 'companies'.
--idrequiredstring
--icpIdrequiredstringThe ICP to judge against. Its ACTIVE version is pinned when the run starts.
--subjectIdsjson arrayjsonThe members to judge. Omit to cover every live member of the list. Ids that are no longer members are dropped, and the run reports what survived as its `total`.
--ignoreIcpMismatchbooleanStart the run even if this list turns out not to match the ICP. Kairon normally stops a run once disqualifications run well past its allowance, and says which criterion did the damage. Set this ONLY after reading that answer and deciding the list is meant to be thin — never on a first attempt, and never to get past a stop you have not looked at.

kairon list qualify-cancel

Stop a qualification run

Full description

Stop a run that is still going.

Flags: list qualify-cancel
FlagTypeWhat it is for
--assetrequiredleads|companiesWhich kind of list: 'leads' (people) or 'companies'.
--idrequiredstring
--runIdrequiredstringThe run to stop.

kairon list qualify-runs

Read a list's qualification run

Full description

How a qualification run is going, or how it ended.

Flags: list qualify-runs
FlagTypeWhat it is for
--assetrequiredleads|companiesWhich kind of list: 'leads' (people) or 'companies'.
--idrequiredstring
--runIdstringA specific run. Omit for this list's most recent one.

kairon list remove

Remove members from a list

Full description

Remove members from a list ({ asset, id, items }).

Flags: list remove
FlagTypeWhat it is for
--assetrequiredleads|companiesWhich kind of list: 'leads' (people) or 'companies'.
--idrequiredstring
--itemsrequiredjson arrayjsonThe rows to remove, each named by any one identity the member could have been added with.

kairon list set

Set members' custom-column values

Full description

Write custom-column values on members ALREADY in a list ({ asset, id, items }, ≤500).

Flags: list set
FlagTypeWhat it is for
--assetrequiredleads|companiesWhich kind of list: 'leads' (people) or 'companies'.
--idrequiredstring
--itemsrequiredjson arrayjsonThe rows to write, each naming its subject plus the custom-column `values` to set on it.

kairon list update

Rename a list, or set its custom columns

Full description

Rename a list and/or replace its custom columns ({ asset, id, name, columns? }).

Flags: list update
FlagTypeWhat it is for
--assetrequiredleads|companiesWhich kind of list: 'leads' (people) or 'companies'.
--idrequiredstring
--namerequiredstringThe list's name. ALWAYS required here, even when you only mean to change `columns` — send the current name back to keep it.
--columnsjson arrayjsonThe WHOLE ordered custom-column set, replacing what is there. Omit to leave the columns alone; [] removes them all. Carry `renameFrom` to rename a column and keep every member's values under the new name.

kairon message

kairon message delete

Delete (retract) a sent message

Full description

Retract a message YOU sent, addressed by the chatId and message id that chat_messages returns.

Flags: message delete
FlagTypeWhat it is for
--chatIdrequiredstring
--messageIdrequiredstringThe message's `id` from chat_messages.

kairon message edit

Edit a sent message

Full description

Replace the body of a message YOU sent, addressed by the chatId and message id that chat_messages returns.

Flags: message edit
FlagTypeWhat it is for
--chatIdrequiredstring
--messageIdrequiredstringThe message's `id` from chat_messages.
--textrequiredstringThe replacement body. It overwrites the message for the recipient too.

kairon message send

Send a direct message

Full description

Send a LinkedIn direct message.

Flags: message send
FlagTypeWhat it is for
--chatIdstringReply in this existing conversation (from chat_list). Use this, or `target`.
--targetjsonjsonOpen a NEW conversation with this person. Use this, or `chatId`.
--textrequiredstringThe message body, exactly as it will be sent. Nothing rewrites it.
--attachmentsjson arrayjson
--clientTokenstringYour own idempotency key. Send the SAME token when retrying after a lost response and the message is not sent twice.

kairon newsletter

kairon newsletter subscribers

List a newsletter’s subscribers

Full description

The people who subscribed to YOUR OWN LinkedIn newsletter — the warmest audience LinkedIn holds, since each one read your writing and chose to keep receiving it.

Flags: newsletter subscribers
FlagTypeWhat it is for
--newsletterrequiredstringThe newsletter, as its URL (https://www.linkedin.com/newsletters/<name>-<id>/) or its bare numeric id. It must be a newsletter the connected seat OWNS — LinkedIn serves this list to the author only.
--cursorstringContinue from a previous page. Omit for the first page.
--countintegerSubscribers per page (default 50, max 100). LinkedIn refuses anything higher — asking for 250 fails the whole call, it does not clamp.
--intoListstringAppend this page’s subscribers to one of your lead lists, by name or id. Combine with `exhaust` to walk the whole audience into it without any of it crossing your context.
--exhaustbooleanPage server-side until the results run out, instead of returning a cursor for you to send back. Requires `intoList`: every page is appended there and NO hits come back, only the totals — which is the point, since neither the hits nor the cursor then cross your context. Stops at `maxResults` and hands back a `cursor` to resume from. Each page still costs one metered action.
--maxResultsintegerHow many hits an `exhaust` run collects before it stops (default 500, max 2000). Ignored without `exhaust`. Raise it when you know the audience is large and you mean to spend the actions.

kairon owner

kairon owner set

Assign an owner to leads or companies

Full description

Make one teammate responsible for the named leads or companies.

Flags: owner set
FlagTypeWhat it is for
--assetrequiredleads|companiesWhich kind of row these items are — people or companies.
--itemsrequiredjson arrayjsonThe rows to assign, each naming its subject by ONE of `memberId`, `url`, or `providerId`.
--ownerrequiredjsonjsonWho to assign every item to — one org member, by `userId` or `email`. The same owner is set on all of them.

kairon pipeline

kairon pipeline board

Read the pipeline board

Full description

Where every one of this org's people stands.

Flags: pipeline board
FlagTypeWhat it is for
--stagenew|invite_sent|connected|contacted|replied|interested|meeting_booked|won|on_hold|lost|discardedReturn this column's people as well as the counts. Omit for counts only, which is what "how is the pipeline doing" needs.
--lastContactedstringNarrow to people last contacted inside a window: `7d`, `30d`, `90d`, `never`, or an explicit `YYYY-MM-DD..YYYY-MM-DD`. Narrows the counts as well as the people.
--cursorstringResume the named column from a previous call — pass the `cursor` it returned.
--limitintegerHow many people to return when `stage` is named.

kairon pipeline stage-set

Move people on the pipeline board

Full description

Record where people now stand: new, invite_sent, connected, contacted, replied, interested, meeting_booked, won, on_hold, lost, or discarded.

Flags: pipeline stage-set
FlagTypeWhat it is for
--itemsrequiredjson arrayjsonThe people to move, each naming its subject by ONE of `memberId`, `url`, or `providerId`.
--stagerequirednew|invite_sent|connected|contacted|replied|interested|meeting_booked|won|on_hold|lost|discardedWhere they now stand. The ladder runs new → invite_sent → connected → contacted → replied → interested → meeting_booked → won | lost. `new` is where a lead nobody has acted on sits; `invite_sent` is an invitation not yet accepted; `connected` is accepted with nothing said yet; `contacted` means there is a message they can read. You may move someone backward as well as forward. Two stages sit outside the ladder, for people nobody is working: `on_hold` keeps them and does nothing for now, and `discarded` means never contact them again — which also adds them to the exclusion list, ending any campaign they are in.

kairon post

kairon post interactions

Who engaged with one post

Full description

Everyone who commented on or reacted to ONE named post, by its LinkedIn URL — the audience a post already earned, each person carrying HOW they engaged (their comment text, or which reaction), which is the warmest opening line outreach get…

Flags: post interactions
FlagTypeWhat it is for
--urlrequiredstringThe post — its LinkedIn permalink (…/posts/… or …/feed/update/…) or its bare `urn:li:activity|ugcPost|share:<id>`. A shortened `lnkd.in` link does NOT work: it carries no post id, so expand it first.
--intoListstringWhere this page's PEOPLE are appended — always a leads list. Only the person is added; the post and how they engaged are why they are here, not members. Pair with `exhaust` to sweep the whole post in one call.
--cursorstring
--exhaustbooleanPage server-side until the results run out, instead of returning a cursor for you to send back. Requires `intoList`: every page is appended there and NO hits come back, only the totals — which is the point, since neither the hits nor the cursor then cross your context. Stops at `maxResults` and hands back a `cursor` to resume from. Each page still costs one metered action.
--maxResultsintegerHow many hits an `exhaust` run collects before it stops (default 500, max 2000). Ignored without `exhaust`. Raise it when you know the audience is large and you mean to spend the actions.

kairon profile

kairon profile posts

A profile's recent posts

Full description

A page of a profile's recent posts (transient — not persisted).

Flags: profile posts
FlagTypeWhat it is for
--targetrequiredjsonjsonWho this acts on — one LinkedIn person or company, named by URL, vanity slug, or provider id.
--cursorstring

kairon qualification

kairon qualification get

Read one member's qualification verdict

Full description

WHY one lead or company carries the verdict it does ({ asset, id }) — the stored scorecard.

Flags: qualification get
FlagTypeWhat it is for
--assetrequiredleads|companiesWhich kind of list: 'leads' (people) or 'companies'.
--idrequiredstringThe member's id, exactly as `list_members` returned it — a lead id under `asset: 'leads'`, a company id under `asset: 'companies'`.

kairon qualification set

Decide about people yourself

Full description

Say whether these people are worth reaching out to, in your own judgement, instead of waiting for Kairon to decide.

Flags: qualification set
FlagTypeWhat it is for
--itemsrequiredjson arrayjsonThe people to decide about, each naming its subject by ONE of `memberId`, `url`, or `providerId`.
--qualificationrequiredjsonjsonYour own verdict: `qualified` to say these people are worth reaching out to even if the ICP disagrees, `disqualified` to keep them out even if it agrees. Pass `null` to take your verdict back and let the ICP decide again. Your verdict wins for as long as it stands: it is what the lists show, what the filters match, and what the campaigns obey — while Kairon goes on judging underneath, so taking it back costs nothing and restores what the ICP had decided.

kairon seat

kairon seat list

List the seats you can send from

Full description

The LinkedIn seats this organization can send FROM — the same list the campaign wizard's sender picker offers, every one connected and ready.

Takes no flags.

kairon signal

kairon skill

kairon skill get

Read one skill

Full description

One playbook in full, as markdown, by a name from skill_list — call that first; an invented name is a 404.

Flags: skill get
FlagTypeWhat it is for
--namerequiredstringWhich playbook to fetch — a `name` from `skill_list`.

kairon skill list

List the available skills

Full description

Kairon's playbooks — how to do each job well: onboarding, Sales Navigator, list building, campaigns, writing messages and posts, what to do next.

Takes no flags.

kairon source

kairon source delete

Detach a lead list's signal source

Full description

Detach the source from a lead list.

Flags: source delete
FlagTypeWhat it is for
--listIdrequiredstringThe lead list whose source this addresses, from list_list or list_create. At most one source exists per list, so this is the source's address — there is no separate source id.

kairon source get

Read a lead list's signal source

Full description

The source attached to a lead list — the recurring feed that makes it a SIGNAL-BASED list, growing itself over time.

Flags: source get
FlagTypeWhat it is for
--listIdrequiredstringThe lead list whose source this addresses, from list_list or list_create. At most one source exists per list, so this is the source's address — there is no separate source id.

kairon source run

Run a lead list's signal source now

Full description

Run the source now instead of waiting for its daily run, and return the run that started.

Flags: source run
FlagTypeWhat it is for
--listIdrequiredstringThe lead list whose source this addresses, from list_list or list_create. At most one source exists per list, so this is the source's address — there is no separate source id.

kairon source runs

Read a signal source's runs, or one run's candidates

Full description

What the source actually did.

Flags: source runs
FlagTypeWhat it is for
--listIdrequiredstringThe lead list whose source this addresses, from list_list or list_create. At most one source exists per list, so this is the source's address — there is no separate source id.
--runIdstringA run id from this tool's own summary page. Given, the result is THAT run's candidates — one row per person the run considered, with the outcome that decided them. Omitted, the result is the run history: when each ran, and how many it appended.
--limitintegerRows per page, 1-100. Defaults to 25.
--cursorstringThe `cursor` from the previous page. A cursor minted for the run history cannot be used against a run's candidates, or the other way round — a mismatched token restarts from the first page rather than paging the wrong thing.

kairon source set

Attach or replace a lead list's signal source

Full description

Make a lead list SIGNAL-BASED, or change the one it already is: give it an ICP to target and the signals that should feed it.

Flags: source set
FlagTypeWhat it is for
--listIdrequiredstringThe lead list whose source this addresses, from list_list or list_create. At most one source exists per list, so this is the source's address — there is no separate source id.
--icpIdrequiredstringThe ICP this source targets, from icp_list. Its ACTIVE version is both the audience searched and the gate every candidate must pass — so revising the ICP changes future runs. Swapping to a DIFFERENT ICP restarts every signal from the top of its feed, because the audience changed.
--signalsrequiredjson arrayjsonThe COMPLETE set of signals this source should carry — at least one. Any signal not listed here is removed. Echo each kept signal with the `id` source_get gave it, or it is treated as new and restarts from the top of its feed.
--statusactive|pausedWhether the source keeps feeding the list. `paused` stops it without losing anything — the signals, their places in their feeds, and every member already found all survive, and resuming carries on where it left off. Use this rather than source_delete to stop a list growing for a while. Omitted leaves the current setting; a new source starts `active`.
--contactFilter.bucketsjson arrayjsonWhich contact-history buckets may enroll: `new` (never contacted), `unanswered` (we messaged, no reply), `in_conversation` (they replied). Unique, at least one. All three means enroll everyone — the default. Anyone outside the selected buckets is skipped, not queued, so a narrow set is why a large audience enrolls almost nobody.
--contactFilter.scopeseat|orgWhose prior-contact history is read: `seat` (this campaign's sending seat) or `org` (every LinkedIn seat). Copying a `seat` filter onto a DIFFERENT seat changes what it means — the new seat's history is empty, so people the original filter excluded now enroll.

kairon tag

kairon tag delete

Delete a tag

Full description

Removes the tag from the organization AND from every lead and company wearing it, in one act.

Flags: tag delete
FlagTypeWhat it is for
--idrequiredstring

kairon tag save

Create or update a tag

Full description

Without id, creates a tag.

Flags: tag save
FlagTypeWhat it is for
--idstring
--namestringThe tag name. Required when creating; on an existing `id` it renames the tag everywhere.
--colorstone|clay|moss|sky|plum|amber|rust|slateDisplay colour for the tag. Omit and one is chosen.

kairon tag set

Set which tags a lead or company carries

Full description

Replaces each named subject's WHOLE tag set — so send every tag it should end up with, not just the new one, and send [] to clear it.

Flags: tag set
FlagTypeWhat it is for
--assetrequiredleads|companiesWhich kind of row these items are — people or companies.
--itemsrequiredjson arrayjsonThe rows to tag, each naming its subject by ONE of `memberId`, `url`, or `providerId`.

kairon webdata

kairon webdata find

Find a data endpoint outside LinkedIn

Full description

Search, in plain words, for a third-party data endpoint Kairon can run for you — data OUTSIDE LinkedIn: tweets by handle, a Google Maps listing, a company's reviews, a person's record at a data broker, and hundreds more.

Flags: webdata find
FlagTypeWhat it is for
--queryrequiredstringWhat data you are after, in plain words. Up to 1000 characters.
--limitintegerHow many matches to return. Default 5, at most 40.

kairon webdata inspect

Read one data endpoint's inputs and price

Full description

The full card for one endpoint webdata_find returned, by its provider and endpoint: what it does, the exact input schema webdata_run expects (path, query and body parameters), the provider's price, a link to the provider's own docs, and an…

Flags: webdata inspect
FlagTypeWhat it is for
--providerrequiredstringThe `provider` slug from `webdata_find`, e.g. `apify`.
--endpointrequiredstringThe `endpoint` path from `webdata_find`, e.g. `/apidojo/tweet-scraper`.

kairon webdata quote

Price a data run before running it

Full description

What THIS organization will pay for one run of an endpoint with exactly this input — the provider's price plus Kairon's margin — and the quoteId that webdata_run requires.

Flags: webdata quote
FlagTypeWhat it is for
--providerrequiredstringThe `provider` slug from `webdata_find`, e.g. `apify`.
--endpointrequiredstringThe `endpoint` path from `webdata_find`, e.g. `/apidojo/tweet-scraper`.
--inputjsonjsonThe endpoint's parameters, as `{ body?, queryParams?, pathParams? }` — each section filled per the matching section of its `webdata_inspect` schema. Omit for an endpoint that takes none. Must be IDENTICAL between `webdata_quote` and `webdata_run`.
--expectedResultsintegerFor a per-result endpoint: how many results you expect back — the cap you put in `input`, or your honest estimate. Turns a per-result price into an `estimatedUsd`.

kairon webdata result

Read back a data run that was still going

Full description

Fetch the outcome of a webdata_run that came back with status other than COMPLETED, FAILED, BLOCKED, STOPPED or TIMED_OUT — a run the provider was still working on when the 60-second wait ended.

Flags: webdata result
FlagTypeWhat it is for
--runIdrequiredstringThe `runId` a still-running `webdata_run` returned.

kairon webdata run

Run a data endpoint (spends the organization's allowance)

Full description

Run one endpoint and get its data back.

Flags: webdata run
FlagTypeWhat it is for
--providerrequiredstringThe `provider` slug from `webdata_find`, e.g. `apify`.
--endpointrequiredstringThe `endpoint` path from `webdata_find`, e.g. `/apidojo/tweet-scraper`.
--inputjsonjsonThe endpoint's parameters, as `{ body?, queryParams?, pathParams? }` — each section filled per the matching section of its `webdata_inspect` schema. Omit for an endpoint that takes none. Must be IDENTICAL between `webdata_quote` and `webdata_run`.
--quoteIdrequiredstringThe `quoteId` `webdata_quote` returned for this exact provider, endpoint and input.

kairon workspace

kairon workspace rename

Rename the workspace

Full description

Change the organization's own display name, exactly as it appears across the Kairon app.

Flags: workspace rename
FlagTypeWhat it is for
--namerequiredstringThe workspace's new display name.

kairon writing

kairon writing check-judge

Judge a draft against the rules, without sending it

Full description

Run one draft past the SAME judge a real send goes through, and get a pass/fail plus a reason for every rule that governs it.

Flags: writing check-judge
FlagTypeWhat it is for
--draftrequiredstringThe exact text to judge, as it would go out.
--surfacerequiredpost|comment|dmWhat kind of writing this is. It decides which rules apply: `general` plus this section and nothing else. `dm` covers every one-to-one message — a campaign send, an inbox reply, an auto-reply.
--contextstringWhat the writer was working from: the post a comment answers, the thread a reply continues, who the message is to. Rules like "does not echo their words back" or "reacts to something specific in the post" CANNOT be judged without it — omit it and they are judged on the draft alone, which reads as a pass.
--templatestringThe operator's own template seed, when this draft was written from one. Its fixed text is then exempt from the style rules (the writer may only fill the slots, so failing it for the operator's own words asks it to fix what it may not touch), and template fidelity is measured against it.

kairon writing check-list

Read the rules every draft is held to

Full description

The rules Kairon holds every draft to, in four sections: general (everything it writes), post, comment, dm (one-to-one messages — campaign sends, inbox replies, auto-replies).

Flags: writing check-list
FlagTypeWhat it is for
--sectionsjson arrayjsonRead only these sections. Omit for all four. `general` applies to everything Kairon writes; the other three govern that surface alone.

kairon writing check-set

Save the Author's own rules for one section

Full description

Replace the Author's OWN rules for ONE section, whole.

Flags: writing check-set
FlagTypeWhat it is for
--sectionrequiredgeneral|post|comment|dmWhich section to replace. A writer is judged against `general` plus its own section and nothing else, so a rule about comments belongs in `comment`, never in `general`.
--checksrequiredjson arrayjsonThe COMPLETE desired list for that section — it REPLACES what is stored. Include every rule to keep, each with its `id` from `writing_check_list` so its history survives, plus any new ones without an id. Omit a rule to delete it. An empty array clears the section. At most 20; Kairon's own rules do not count against it.

kairon writing check-shared-set

Switch one of Kairon's own rules on or off

Full description

Switch one of Kairon's SHARED rules on or off for this Author, by a key from writing_check_list.

Flags: writing check-shared-set
FlagTypeWhat it is for
--keyrequiredstringA shared rule's `key` from `writing_check_list` (e.g. `comment.no_first_name_opener`). A key that is not a real shared rule is refused rather than stored.
--enabledrequiredboolean`false` switches Kairon's rule off for this Author; `true` switches it back on.

Flags

Flags are derived from each tool input schema, not written by hand, which is why they match the tool exactly. Three shapes get three treatments.

Scalars are plain flags

A string, number or boolean becomes --flag and takes its value literally. An enum shows its choices in the type column.

Nested objects are dot paths

A tool input nests freely, so a nested field becomes --config.keyword rather than a flattened name that could collide with another tool field.

Arrays and unions take JSON

There is no honest flat spelling for "an array of either a member id or a URL", so those flags take JSON text. They are marked json in the table below.

Typos are caught before the network

Input is checked against the cached schema before any call, so a misspelled flag costs nothing and names itself: it tells you the option is unknown and suggests the one you meant.

Every command also accepts

--server <url>
Target a specific Kairon (default: KAIRON_API_URL, else production)
--json <json>
Pass the entire tool input as one JSON object
--refresh
Re-fetch the tool catalogue before running
-h, --help
Show help
-v, --version
Show the CLI version
And the escape hatch, when flags get in the way
kairon list create --json '{"asset":"leads","name":"Q3"}'

Output and exit codes

The result is JSON on stdout. Everything a human reads goes to stderr, so a pipe stays clean with no flags. A command that succeeds exits 0. A command that fails exits non-zero, writes nothing at all to stdout, and leads with a stable error code you can branch on rather than prose you would have to pattern-match.

Piping works with no flags
kairon campaign stats --id camp_… | jq '.funnel'
A failure names its code
$ kairon list get --asset leads --id list_01… ; echo "exit=$?"
kairon list get failed: COMMON_NOT_FOUND
{
  "code": "COMMON_NOT_FOUND",
  "params": { "resource": "lead_list" }
}
exit=1

Environment and headless use

For CI, a cron job or a container, there is no browser to open. Pass a token instead and the CLI needs no stored credentials at all.

KAIRON_TOKEN
Use this access token and ignore stored credentials entirely.
KAIRON_API_URL
Default server. The --server flag beats it, and production is the fallback.
KAIRON_CONFIG_DIR
Where credentials live. Defaults to ~/.kairon.
No ~/.kairon needed
KAIRON_TOKEN=<access token> kairon whoami

Acting as someone else

Kairon staff can act as any user; an owner or admin can act as a member of their own organization who does not outrank them. One command in, one command out, nothing to type in between.

kairon impersonate list --search acmekairon impersonate start --user [email protected]kairon chat listkairon impersonate stop

The mode belongs to this login, not to you

Signing in registers a fresh client each time, so your terminal, your assistant and your cron each have their own. Acting as someone here does not redirect the 3am job.

It ends 60 minutes after your last call

Not 60 minutes after it started. Working never interrupts you; leaving for lunch ends it.

Every command under it says so on stderr

One line naming who you are acting as. stdout is untouched, so a pipe keeps working.

Only someone with a LinkedIn seat

Acting as a seatless user would reach nothing, so impersonate list tags each candidate with whether they have one.

Troubleshooting

Every command says there is no seat

The account has no LinkedIn account connected, or the plan is not active. Connect one in Kairon and run kairon whoami again. Commands act as that seat, so nothing works without it.

A command does not exist

Commands come from the server, cached per server URL under ~/.kairon/tools/, and the cache has no expiry because a tool set changes on deploy and not on a schedule. Run the command again with --refresh to re-fetch it.

It asks you to sign in again

Access tokens last 30 days and refresh silently. When the refresh token finally expires after 90 days you get one sentence and the command to run.

Signing out does not revoke anything

kairon logout deletes the local credentials. The server issues opaque tokens with no revocation endpoint, so the access token stays valid there until it expires. The command says so rather than implying a revocation that did not happen.