Kairon
About
Build prospecting lists from buying signals, enrich them, and run LinkedIn outreach.
Details
- Author
- Unknown
- Categories
- Marketing, Other
Jump to
Connect with OAuth (Claude web and desktop)
Connect from a terminal (Claude Code, Cursor, scripts)
claude mcp add --transport http kairon https://app.heykairon.com/mcp npm i -g @kairon/cli && kairon login
- Find agencies in Spain hiring an SDR this month and save them to a list called Q3.
- How is the Q3 campaign doing?
- Who answered me this week, and what did they say?
Everything an assistant can do once it is connected. If something is not on this list, it cannot do it.
This list is generated from the server on every release, so it cannot fall behind. The text stays in English because that is what your assistant reads.
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. Cached globally; pass the returned urn to search_sales_navigator, or let it resolve names for you.
Create an ICP — a target thesis spanning the companies to reach and the people inside them — at version 1. Criteria are optional: a name plus an emptydefinitionis a valid coarse ICP you can sharpen later withicp_update. Read theicp-definitionskill first: it carries the check rules and the filter defaults, and an ICP written without them disqualifies everyone while looking right.
Soft-delete an ICP. ReturnsreferencedByCampaigns— how many campaigns referenced it as their enrollment gate (any status, draft included) and no longer have one. The delete always proceeds, so check that number and tell the operator if it is not zero: any of those campaigns that is running just widened its audience.
Read one ICP and its latest (active) version byicpId.
List this org's ICPs, each with its latest (active) version's criteria and conditions.
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. Callicp_getfirst — its conditions are the checks to answer, and their ids are whatresults[].criterionIdmust carry. Answer EVERY check of the sides judged or the call is refused, never scored: a missing answer reads as a rejection.rationaleis required. Pace a large audit against the returnedheadroom.
Revise an ICP: mints a new immutable version, which becomes the active one. This replaces thedefinitionwholesale, so a field you leave out is cleared, not carried forward — callicp_getfirst and send back the complete definition, changing only what you mean to change. Past versions stay readable, so earlier verdicts remain explainable. Read theicp-definitionskill before you change the AI checks — it decides whether the new version still qualifies anyone.
Fetch a LinkedIn person OR company through the caller's connected account — whichever thetargetaddresses ({ type, value }: url, public_identifier, or provider_id). The kind is detected from the target; passkind: 'company'for a company addressed by universal name or numeric id. Returns complete data, metered and cached (refresh: trueforces a live read), plusheadroom— what is LEFT on this seat. Budget a large audit against the SMALLEST number in it. For a person,withRecommendations: truealso returns who vouched for them — each recommender named and addressable, the strongest warm-intro path LinkedIn publishes.
Add members to a list ({ asset, id, items }, up to 500). An item is{ memberId }(already in the org),{ url }(a pasted LinkedIn URL — instant and free, no provider call), or{ snapshot }(a hit fromsearch_people/search_sales_navigator/signal_search, passed as-is with itsproviderId, which is what makes the saved list show the person rather than a bare id). Re-adding a current member converges. This is how you assemble an audience after a search.
Create a named list — the campaign targeting unit.asset: 'leads'for people,asset: 'companies'for companies (build the company set first, then find people inside it by passing the list's id tosearch_sales_navigatorascompanyLists). Names are unique per org and asset, case-insensitive; a taken name fails with the existing list's id, never a silent merge. Build membership withlist_add.
Soft-delete a list byasset+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 immediately free for reuse.
One list byasset+id: its name, live member count, created date, and its orderedcolumns(the list's own custom columns — free-text fields you attach to its members). Out-of-org or deleted ids are not found. To read who is IN it, use list_members, whose rows carry each member's values for those columns.
Read this org's lists of oneasset, keyset-paginated, newest first, each with its live member count.qmatches the list name (case-insensitive). Page with the returnedcursor. Deleted lists never appear.
One keyset page of a list's members ({ asset, id, q?, tags?, cursor?, limit? }), newest-added first. Each row is{ id, title, subtitle, location, linkedinUrl, target, company, companyId, companyRecordId, values, tags, owner }— the id removes it with list_remove or writes with list_set,valuesholds this list's custom columns,tagsthe org-wide labels the person wears,ownerwho works it. A leads row carries its member's EMPLOYER —companyis the name,companyIdLinkedIn's own id, the same one a people-search hit reports — so grouping or pruning a list you already hold needs no search re-run. To load a member, pass itstargetto linkedin_fetch AS-IS: it carries the right handle and its ownkind. Do NOT fetch bylinkedinUrl— that is the human link, null for anyone found through Sales Navigator, who is fetchable all the same.tagsin the query keeps only members carrying EVERY tag named. Page with the returnedcursor.
Remove members from a list ({ asset, id, items }). Each item names ONE member however you hold it:{ memberId },{ url },{ providerId }, or{ snapshot }(a hit passed back unedited — only its providerId is read). Deletes only the membership; the leads/companies stay in the org pool and their other lists. Per item:removed,not_member, ornot_found.
Write custom-column values on members ALREADY in a list ({ asset, id, items }, ≤500). Each item names one member —{ memberId },{ url },{ providerId }or{ snapshot }— plusvalues, keyed by COLUMN NAME.
Merges: names you send are written, names you omit keep what they had, the empty string clears. Create columns first with list_update; a name this list lacks is skipped and echoed inignored. Per item:set,not_member,not_found. Read them back with list_members. When a value is a fact you looked up rather than one the operator told you, put the source in the cell — the quote or number, the URL you read it on, and the date. Values the operator owns (a stage, a note, your own summary) need none of that.
Rename a list and/or replace its custom columns ({ asset, id, name, columns? }). A name another live list of that asset holds fails with that list's id.
Custom columns are free-text fields on the members of THIS list, addressed by NAME.columnsreplaces the WHOLE ordered set: omit it to leave them alone,[]removes all.{ name, renameFrom }renames one and keeps its values; withoutrenameFromthe old column and everything in it is destroyed. Max 20, names ≤40 chars.
Make one teammate responsible for the named leads or companies. Name them byemail(what you usually have) oruserId; passowner: nullto leave them unassigned. The owner must be a current member of the organization. This only labels — it grants no permission and does not decide which seat sends. Never creates a lead: an unknown url comes back not_found.
Where everyone your campaigns reached now stands. Returns a count for each stage — contacted, replied, interested, meeting_booked, won, lost — and, when you name astage, that column's people with their owner and when they were last contacted. UselastContactedto ask about a window ("who at interested have we not touched in 30 days"). A column comes back one page at a time:hasMoresays whether more remain, and passing the returnedcursorback reads the next page. Reads only: it never moves anyone.
Record where people now stand: contacted, replied, interested, meeting_booked, won, or lost. Name them byurl(what you usually have),memberId, orproviderId. Moving someone backward is allowed. Kairon advances the early stages on its own from replies, so use this for the judgment calls it cannot make — a meeting booked, a deal won or lost. Never creates a lead: an unknown url comes back not_found.
Detach the source from a lead list. The list KEEPS every member the source already found — only the feed stops, so the list becomes an ordinary static list rather than losing anything. Frees the one-source-per-list slot, so a new source can be attached afterwards withsource_set. To pause a source temporarily instead,source_setit to a paused status.
The source attached to a lead list — the recurring feed that makes it a SIGNAL-BASED list, growing itself over time. Returnsnullwhen the list has none, which means it is an ordinary static list, not that anything failed. The result carries the ICP the source targets and every signal on it, each with its ownid— those ids are whatsource_setechoes back to keep a signal's place in its feed. Read this before changing a source:source_setreplaces the whole signal set.
Run the source now instead of waiting for its daily run, and return the run that started. This SPENDS — it is a real provider search on the seat the source was created on, and it is the only tool here that costs anything. Attaching a source does not run it, so this is how a newly attached list first fills. Refused while the source is paused, or while one of its runs is already in flight; a run is not instant, so read what it did withsource_runsrather than expecting members back from this call.
What the source actually did. WithoutrunId, one page of run history newest-first — when each run happened and how many people it appended. WithrunId, that run's CANDIDATES instead: one row per person it considered and the outcome that decided them, which is where "why is this person in my list, and why is that one not?" is answered. Page with the returnedcursor; a run's candidates come back in the order the run touched them, so the page order IS the funnel order. Reads only — it never starts a run and spends nothing.
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. This REPLACES the whole signal set — any signal you do not list is removed — so callsource_getfirst and send back every signal you want to keep, each with theidit gave you. A kept signal holds its place in its feed; one sent without itsidis treated as new and restarts from the top, which costs a re-walk. Attaching does NOT run the source: nothing is searched or spent untilsource_run. Swapping to a different ICP restarts every signal, because the audience changed. Refusals are total — a bad config or an unknown signal id leaves the source exactly as it was.
Removes the tag from the organization AND from every lead and company wearing it, in one act. Reports how many of each lost it. The name becomes free to use again immediately. This does not delete any lead or company.
Withoutid, creates a tag. Withid, renames and/or recolors that one — and a rename carries it across every lead and company already wearing it, in one act. Names are unique per organization, compared case-insensitively. Colors are palette names: stone, clay, moss, sky, plum, amber, rust, slate.
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. Name a subject bymemberId,url, orproviderId, exactly aslist_removedoes. Tags are named by NAME and must already exist (tag_savefirst); an unknown name fails the call rather than creating one. This never creates a lead or a company: an unknown url comes backnot_found.
THIS STARTS SENDING MESSAGES TO REAL PEOPLE ON LINKEDIN, and a message already sent cannot be un-sent. Activates adraftorpausedcampaign;startNow(default true) enrolls today's first batch instead of waiting for the next send window. Refused withCAMPAIGN_ACTIVATION_BLOCKED(409) unless all four preconditions hold —params.unmetnames every one that does not, as data you can branch on. Activating an active campaign is a no-op, safe to retry. See thecampaign-buildingskill.
Per-lead slot coverage. A{{@slot}}in a send body (either draft mode — anai_templateseed fills it before the writer runs) is filled by each lead's LIST COLUMN of the same name (case-insensitive) — write values withlist_set, never a separate copy call. Returnscoverage: { slots, ready, missing }over the whole audience plus a page of leads;missingOnlykeeps just the ones still short — "what is left to write".campaign_runscannot answer this: an unfilled lead never enrolls, so it has no run, which is why such a campaign shows a largeaudienceSize,queuedCount: 0, and sends nothing. 400 if the published graph declares no slots.
Create a draft campaign: anameand the sending seat (channelAccountId, a connected LinkedIn account in this org —seat_listreturns the ones you can pick), optionally the ICP to gate enrollment on (icpId). There is no silent default — ask the operator which ICP to gate on (useicp_listto show the options, and its most-used one as your suggestion) before calling, or confirm they want no gate; omittingicpIdcreates an ungated campaign. Returns a campaign indraftwith no audience and no sequence — bind lists withcampaign_update, thencampaign_set_graph, thencampaign_publish. Thecampaign-buildingskill has the full order and the defaults.
Read ONE campaign completely enough to REBUILD it. Returns its configuration, the bound lead lists, and BOTH graphs:published(what the engine is sending now) anddraft(whatcampaign_set_graphwould overwrite), each in exactly the shape that tool accepts. To copy a campaign, replay what this returns — never retype a sequence someone described, because node configs carry slots, draft modes and limits invisible in prose. Thecampaign-buildingskill has the copy recipe.
Enroll one person from the campaign's audience now, instead of waiting for tomorrow's batch. It skips the DAILY PACING, never the gate: the same ICP and enrollment-filter checks the morning tick runs still judge the lead. So the outcome is data, not a promise —enrolled,skipped(with the reason, e.g. they do not match the ICP),excluded, orfailed. A lead who does not fit is marked Skipped rather than messaged.
List this org's campaigns, newest first, keyset-paginated —{ q?, status?, cursor?, limit? }.qmatches the campaign name;statusfilters to one ofdraft|active|paused|archived; page with the returnedcursor(null on the last page). Each item is the campaign’s configuration — the same shape campaign_create returns — WITHOUT its sequence graph; read one campaign in full with campaign_get. This is how you find a campaign the operator named but whoseidyou were not given.
Pause an active campaign. Nothing new fires; runs already in flight wait at their current step and resume there if the campaign is reactivated. Nothing already sent is recalled. Idempotent — pausing a paused campaign returns it unchanged. Refused (400) if the campaign isdraftorarchived: only anactiveone can be paused.
READ THE MESSAGE BEFORE YOU ACTIVATE. Drafts one saved step for up to 3 real audience leads through the SAME pipeline a real send uses, and sends nothing. Name the step by itsnodeKeyfromcampaign_get; omitleadIdsto sample the audience. Per lead you get thebodythat would fire, itsreasoningandevidence, anerrorwhere no message could be produced, and the send-verifier'sverificationverdict (advisory here — a preview never blocks). The reply names which graph it read. SLOW: expect up to ~2 minutes, because the LinkedIn profile reads behind the drafts are paced one at a time per seat — do not retry a call that has not returned.
Validate and publish the campaign's draft graph. Publish can FAIL validation, and it fails as DATA, not as an exception:published: falsewith EVERY error inerrors(each naming its code and node), so one pass fixes all of them. On successversioncarries the freshly published graph.migrateInFlightadditionally repoints in-flight runs onto it by node key, canceling any run parked on a node the new version removed. Thecampaign-buildingskill lists the error codes.
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 budget. Filter withkindandcampaignId. This is where therunIdandnodeRunIdthat every per-run tool takes come from — areviewrow's parked send has no other index. Each row'sbodyholds the words that will go out — an invite's note, a message, a comment — told apart byactionType, so "what are we about to send" is one call. It is a live WINDOW:limitapplies per kind so one busy kind cannot hide another, andwindowCapped: truemeanstotalis a floor. To read one campaign exhaustively usecampaign_runs, which pages with a cursor; for one row's reasoning, citations and timeline, open it withcampaign_run_get.
THIS SENDS THE MESSAGE. On amanualcampaign every send waits for a human; this releases one. Read the draft first withcampaign_run_get— approving without reading is how an agent sends words nobody checked. If the send was held by the pre-send check, approving is an OVERRIDE and is recorded against your name on the message itself. Refused (409) if the run is not awaiting approval, or if its campaign is paused — resume it first. Edit the words first withcampaign_send_edit.
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. Nothing already SENT is recalled. This is final for that campaign — a canceled person moves toskippedand it will not pick them up again (re-adding them via another bound list is the only way back). Ids are DEDUPED, so readcanceledrather than counting what you sent. Every distinct id comes back with its own outcome:canceled,not_found,not_cancelableonce a run has already finished, orfailed— that one alone is unknown, so re-send it. A wrong-KIND id never gets that far: ids are checked for thesrun_prefix at the door, so a feed row’snodeRunIdis refused outright rather than counted as a retryable failure. Idempotent on an already-canceled run.
Read ONE sequence run in full, by anidfromcampaign_runs. Returns the lead, the sending seat, the run's position, and the node-by-node timeline with each step's status and timing. When a send is drafted or already out,bodycarries the actual text, with its reasoning and evidence for an AI draft. This is how you answer "what did we actually say to this person, and what happened next" —campaign_runsgives you the page, this gives you the story.
Cut short thedelaystep a run is parked on so the sequence carries straight on to its next step. Refused on a run that is NOT on a delay: a booked send iscampaign_send_now, and await_connection_acceptedcannot be skipped at all — the skip would not match the wait it is parked on, and no amount of skipping makes someone accept an invite. DO NOT RETRY a refusal: once the wait is skipped the run has moved past it, so a second call reportsCAMPAIGN_RUN_NOT_ACTIONABLEfor work that already succeeded.
Read the campaign's sequence runs — one per enrolled lead — keyset-paginated.bucketfilters to one activity tab (waiting,didnt_accept,didnt_answer,replied,interested,skipped,error); omit it for every run. Each run carries the lead, where it currently stands, when it acts next, its outcome once terminal, and the error or skip detail where relevant. Page with the returnedcursor.campaign_statstells you which bucket is worth reading.
Replace the words a drafted or scheduled send will fire with — the edited text is exactly what goes out, never re-written afterwards. Addressed bynodeRunId(fromcampaign_review_feed), NOT the run id. Refused (409) once the send is out; there is no editing a sent message. On a send held by the pre-send check, editing CLEARS the warning: it described a sentence that no longer exists, and your own words are yours. An InMail may carry a newsubject; every other send type refuses one.
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. It is a HAND-OFF — the run is armed and the sender fires it a moment later, socampaign_review_feedshowsexpediteNextSend: trueuntil it goes. DO NOT RETRY: a second call is refused withCAMPAIGN_RUN_NOT_ACTIONABLEbecause the run is already armed, which means the send is on its way, not that it failed. Only a run parked on a send with a reviewed draft can be expedited; one sitting on a delay is refused (usecampaign_run_skip_wait).
Replace the campaign's editable DRAFT sequence graph IN FULL — this overwrites, it is not a patch. Nodes are{ key, type, config }with a stablekeyyou choose; edges are{ fromKey, toKey, condition }routing one node's outcome to the next. Layout is automatic. Which conditions a type emits, and which types it may connect to, are fixed rules — theedgesfield carries the table, and a violation is refused (400). Thecampaign-buildingskill has the order to build a campaign in, and how to write the copy. Saving is not publishing.
Edit a campaign's configuration — any subset of its fields; omitted ones are unchanged. This is also how an audience is bound, vialeadListIds, and that binding is additive: a bound list you leave out is NOT unbound, because unbinding would cancel every in-flight run drawn from it. It is also how a campaign is pointed at the bet it serves, viainitiativeId(nulldetaches) — do that for every campaign you build under an initiative, or the bet reports an empty funnel forever.sendWindowis required beforecampaign_activatewill run.autonomyis 'autopilot' (sends fire on schedule) or 'manual' (each send waits for approval, except a note-less invite).
Remove one checkpoint from a bet’s history. Deleting the newest one moves the initiative’s health back to whatever the one before it said — or to unknown when there is none. Use it for an entry published against the wrong bet or by accident, never to tidy away a verdict somebody did not like.
Fix what one checkpoint says, or the verdict it froze — for a typo or a verdict typed by mistake. The entry keeps the date it was made and its author, and every surface marks it as edited. A checkpoint that has gone out of date is NOT fixed here: publish a newer one instead, so the history shows the change of mind. A rewritten body carries @mentions the same way checkpoint_publish does.
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. The verdict is FROZEN — publishing a newer one never rewrites an older one, which is what makes the history evidence. Read the gtm-checkpoint skill first; it carries the rule about judging volume before rates.
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. Read this before publishing a checkpoint — the last one says what was recommended, and whether it was done is the question a checkpoint has to answer.
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. Read this before proposing work — a campaign that serves no bet is how fifteen open fronts happen.
Write down a bet: a name, the thinking in markdown, and who is accountable for it. OmitinitiativeIdto create, pass one to edit. Health is NOT a field here and never will be — an initiative reports the verdict of its newest checkpoint, so the way to move it is checkpoint_publish. Read the gtm-initiative skill before creating one.
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 interested RATES. This is the account-level questioncampaign_statscannot answer: that one needs a campaign named first, and this spans every campaign and every seat. Narrow to one seat withuserId(from the breakdown). Days are bucketed in your own stored timezone, not UTC.
List LinkedIn conversations, newest activity first. Defaults to YOUR OWN seat — passscope: "org"for every seat in the organization, orseatIdfor one teammate. Filter by unread, leads-only, orq, which matches the counterpart's NAME and does not search message text. Each chat carries theidthatchat_messagestakes, the counterpart, any linked lead, and a last-message preview. Page with the returnedcursor. Reading is free: never metered, never against a sending limit.
Read one conversation's messages, newest first, by thechatIdfrom chat_list. Each message hasdirection("in" = from them, "out" = from you),body,sentAt, and anyattachments; a message the counterpart deleted comes back withdeleted: trueand an empty body. Page further back in history with the returnedcursor. Opening a thread may pull fresh messages from LinkedIn, which is not metered and does not count against any sending limit.
Send an InMail (optional subject) to the persontargetaddresses. Requires the Sales Navigator seat and a live InMail credit; refused fast without either. An attachment is one form or the other: inlinedataBase64(at most ONE per send, ≤3 MB, it rides in the JSON body) or aurlwe fetch server-side (up to 5, ≤15 MB each). Reach forurlwhen you cannot produce base64. This contacts a real person AND spends a credit, with no approval step.
Send a LinkedIn connection invitation (optional note) through the caller's account. Address the recipient withtarget—{ type, value }(url, public_identifier, or provider_id). Refused if already connected/invited; deduped per recipient; capped daily. This contacts a real person — there is no human approval step.
Retract a message YOU sent, addressed by thechatIdand messageidthat chat_messages returns. Only outbound messages can be deleted — an inbound one is a not-found. Removes it from LinkedIn and marks it deleted in the Kairon conversation. Idempotent. Metered.
Replace the body of a message YOU sent, addressed by thechatIdand messageidthat chat_messages returns. Only outbound messages can be edited — an inbound one is a not-found. LinkedIn allows this on Classic messages within about an hour of sending; later attempts are refused by LinkedIn. Updates the conversation in Kairon as well as on LinkedIn. Metered.
Send a LinkedIn direct message. Pass EXACTLY ONE ofchatId— reply in an existing conversation (anidfromchat_list), which is what you want when it exists — ortargetto open a NEW one, allowed only with a 1st-degree connection or an open profile. Attachments: at most ONE inline base64 file (≤3 MB, it rides in the JSON body), up to 5 total when the rest are https urls we fetch server-side. ReturnschatIdand the persisted message. This contacts a real person, with no approval step.
Who this connection acts as: the user, their organization, the seat (seatIdplus its state:connected,none, orlocked— needs a subscription),linkedin, how they are addressed there, andcapabilities, what the seat can do. Call it afterimpersonate_start/impersonate_stopand before anything that sends, so you know whose account a message leaves from.seatalso explains a refusal.capabilities.salesNavigatortrue meanssearch_sales_navigatorruns on THIS seat, sonetworkDistance/connectionsOfmeasure the degree from this person; false means shared capacity, which refuses both.linkedinholds the handle and display facts only; whenhydratedis false, pass itspublicIdentifiertolinkedin_fetchfor the full profile.seatIdnames your own account even when it is not currently sendable (one waiting on a reconnect or a 2FA checkpoint keeps its id here);seat_listis the seats that CAN send right now.
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. Staff see every tenant; an org owner or admin sees the members of the organizations they govern.searchmatches name, email or organization.
Act as another user from now on: every later call runs as them, on their LinkedIn seat and their organization. Staff may act as anyone; an org owner or admin only as a member of their own organization, never one ranking above them.useris an email address or a user id. Calling it again switches target. The mode belongs to this client alone and ends 60 minutes after your last call, or onimpersonate_stop.
Go back to being yourself. Succeeds whether or not you were acting as anyone, and reports which it was.
The LinkedIn seats this organization can send FROM — the same list the campaign wizard's sender picker offers, every one connected and ready. Call it BEFOREcampaign_create: a seat'sidis exactly what that tool takes aschannelAccountId, and whatchat_listtakes asseatId.ownerNameis the teammate it belongs to,ownerUserIdis that teammate's own id (what an @mention in an initiative or checkpoint body stores), andisSelfmarks your own; any member may send from any of them, and the chosen seat's owner is the campaign's author.hasSalesNavigatorsays whether an InMail step is legal on that seat.authCountryis the country it authenticates from — use it to open a send window on noon where the seat lives. An empty list means no seat here is ready to send from. If you expected one, ask the operator to contact support at[](https://heykairon.com/cdn-cgi/l/email-protection)<<<[email protected]. Free: it reads nothing on LinkedIn and spends no daily limit.
What the operator has told Kairon: their content checks (what a good post must satisfy), their competitor and source URLs, their writing guidelines (voice), and their organization's value proposition, lead magnet and learnings log. Read this FIRST when deciding what they should work on next, and ground every draft in it — a message written without it is generic. Namesectionsto read only some.
Replace one or more sections of what the operator has told Kairon:checks,competitorUrls,sourceUrls,writingGuidelines,valueProposition,leadMagnet,learnings. Each section you name is replaced WHOLE; each one you omit is untouched. When the operator gives you a whole section, send it straight here. Only when ADDING to what is already stored (one more item, anotherlearningsentry) callconfig_getfirst and send the merged text. An empty array or string clears a section.
One product doc in full, as markdown, by aslugfromdoc_list. Ground what you tell the operator in what it says — do not invent features, steps, limits or settings that are not in it, and when the doc states a constraint, report the constraint rather than working around it. Pair withskill_getwhen the question is not "what does Kairon do" but "how do I do this well".
…
Sign in to leave a review
Use Google, GitHub, or an email account so ratings stay tied to real people.
No reviews posted yet.



