Channels API
Channels are daemon-owned public conversation resources. They are separate from sessions:- channels hold the public conversation log
- sessions still execute the work
- channel-triggered agent work shows up as normal runs of kind
ChannelDelivery
Endpoint inventory
Channels:POST /v1/channelsGET /v1/channelsGET /v1/channels/{channel_id}PUT /v1/channels/{channel_id}DELETE /v1/channels/{channel_id}
GET /v1/channels/{channel_id}/membersPOST /v1/channels/{channel_id}/membersDELETE /v1/channels/{channel_id}/members/{member_id}
GET /v1/channels/{channel_id}/messagesPOST /v1/channels/{channel_id}/messagesPOST /v1/channels/{channel_id}/messages/{message_id}/reactionsDELETE /v1/channels/{channel_id}/messages/{message_id}/reactions
GET /v1/channels/{channel_id}/leasesGET /v1/channels/{channel_id}/stimuliPOST /v1/channels/{channel_id}/stimuliGET /v1/channels/{channel_id}/thread-work
List channels
GET /v1/channels supports one optional query parameter:
query
Create a channel
POST /v1/channels accepts:
channel_idoptional caller-selected stable identifiertitlerequired user-visible titledescriptionoptional short summarypurposeoptional longer purposepinned_asset_idsoptional daemon-owned assets pinned in the channel headercreated_byoptional creator actor idmembersoptional initial rosterautonomy_policyoptional public turn policydefault_participation_modeoptional default member modemetadataoptional caller metadata
max_parallel_public_speakers = 1max_agent_replies_per_human_message = 3member_cooldown_ms = 15000lease_timeout_ms = 60000max_pending_stimuli = 32max_autonomous_root_posts_per_hour = 4max_active_autonomous_roots = 1quiet_period_ms_after_root_post = 300000default_participation_mode = selected_only
- the autonomy policy is persisted and returned through the API
- the current daemon still enforces one active public speaker per thread, so
max_parallel_public_speakersis effectively fixed at1today
Update a channel
PUT /v1/channels/{channel_id} accepts partial updates for:
titledescriptionpurposepinned_asset_idsautonomy_policydefault_participation_modepausedmetadata
Channel members
Member creation and update reuse the same request shape throughPOST /v1/channels/{channel_id}/members.
Fields:
member_idrequired stable identifier inside the channelmember_kindrequired:human_actororsessiondisplay_name_modeoptional forsessionmembers:manualorfollow_agentdisplay_namerequiredsession_idoptional forsessionmembers; when omitted, the daemon falls back tomember_idactor_idoptional forhuman_actormembers; when omitted, the daemon falls back tomember_idroleoptional arbitration hintexpertise_tagsoptional arbitration hintsparticipation_modeoptional member-level participation overridemutedoptional autonomous mute flag
manual_onlyselected_onlyprefer_selectedalways_listen
manualkeeps the channel-local roster label exactly as storedfollow_agentkeeps the roster label aligned with the current visible agent nickname for that bound session- message history is immutable, so changing the roster label does not rewrite older public messages
DELETE /v1/channels/{channel_id}/members/{member_id} removes one member from the roster.
One important current interaction with projects:
- if a linked project channel enables
mirror_members, mirrored project members appear here as ordinary channel members with ids shaped likeproject:{project_id}:{member_id}
Post a public message
POST /v1/channels/{channel_id}/messages accepts:
sender_actor_idrequired stable sender idsender_display_nameoptional display labelsender_session_idoptional bound sender session idthread_root_message_idoptional target thread rootreply_to_message_idoptional direct parent messageaddressed_member_idsoptional ordered first-turn priority membersinput_itemsoptional ordered multimodal contentcontentoptional plain-text fallback wheninput_itemsis emptymetadataoptional caller metadata
- the sender must already be a channel member
sender_session_idis required when posting as a session-backed senderinput_itemsandcontentfollow the same general validation model as session input- for non-session senders,
board_referenceitems are rejected because board resolution is session-scoped - explicit
addressed_member_idsare preserved on the durable message record and influence the next public-turn arbitration
List public messages
GET /v1/channels/{channel_id}/messages supports:
thread_root_message_idquerylimit
thread_root_message_id restricts the result to one public thread.
query matches sender identifiers, sender display names, message text, and attachment file names.
limit trims the result to the latest matching messages.
Only root messages define new main-feed subjects. Replies stay attached to their canonical thread_root_message_id.
Reactions
POST /v1/channels/{channel_id}/messages/{message_id}/reactions and DELETE on the same path both use:
actor_idmust identify a current channel member- reactions are aggregated on the message view
- reactions are durable public state, not local UI-only signals
Public turn leases
GET /v1/channels/{channel_id}/leases returns the current public thread leases used by the daemon to prevent reply storms.
This is primarily an operational and debugging surface.
Fields include:
turn_idchannel_idthread_root_message_idorigin_message_idholder_session_idactive_run_idremaining_reply_budgetexpires_at_msqueued_candidate_session_idslast_human_message_idagent_reply_count_since_last_human
Stimuli
GET /v1/channels/{channel_id}/stimuli supports:
thread_root_message_idstatelimit
pendingclaimeddispatchedcoalescedsupersededcancelled
POST /v1/channels/{channel_id}/stimuli accepts:
scoperequired:channelorthreadthread_root_message_idrequired for thread-scoped stimulikindrequiredvisibility_hintoptional:auto,main, orthreadcontentrequired public textaddressed_member_idsoptional ordered first-turn priority memberssender_session_idoptional session-backed sender identitysender_actor_idoptional non-session sender identitysender_display_nameoptional non-session sender labelsource_kindoptional stable source categorysource_refoptional stable source identifierdedupe_keyoptional equivalence key for dedupe and supersessionprogress_keyoptional progress supersession keyavailable_at_msoptional deferred availability timestampexpires_at_msoptional cancellation cutoffmetadataoptional caller metadata
agent_ideaschedule_fireschedule_resulttask_completedreview_completedsidechain_milestoneobservation_materializedthread_idle_followupresult_summarysupersession_notice
channelscope may open one new top-level subject in the main feedthreadscope may only continue one canonical root threadthread_root_message_idis required forthreadscope and rejected forchannelscope- the daemon deduplicates equivalent queued stimuli and can reuse one already materialized post during recovery instead of duplicating it
- a stimulus is operational wake-up state, not a second public conversation object
Canonical thread-work state
GET /v1/channels/{channel_id}/thread-work supports:
thread_root_message_id
channel_idthread_root_message_idtopic_kindstatusowner_session_idinitiative_keysource_kindsource_reflast_stimulus_idlast_signal_at_mslast_main_promotion_at_msbindingsprogress_snapshotsmetadata
topic_kind values are:
humanautonomous_rootsummary
proposedactivereviewingcompletedsuperseded
schedulerunsidechain_agenttaskobservation
progress_snapshots expose one durable supersedable view per progress stream, keyed by progress_key.
Message and channel views
Channel detail responses expose:- the flattened
summary memberspinned_asset_idscreated_byautonomy_policydefault_participation_modemetadata
message_idchannel_idthread_root_message_idreply_to_message_idsendersender_session_idaddressed_member_idsoutputcreated_at_msreactionsmetadata
output is a normal daemon RichOutput, so public messages can carry text plus ordered attachment parts.
Deletion semantics
DELETE /v1/channels/{channel_id} removes:
- the channel record
- the public message log
- reaction state
- public turn leases
- queued stimuli
- canonical thread-work state
- the compact channel index entry
Relationship to runs
A channel does not execute work on its own. When the daemon decides that a session member should speak publicly, it creates a run of kindChannelDelivery in that member session. Inspect those runs through the normal runs API:
GET /v1/runsGET /v1/runs/{run_id}
CLI surface
The daemon CLI exposes the same channel resources and operations, with one current limitation: channelautonomy_policy is configurable through HTTP today but not yet through dedicated CLI flags.
Available commands:
kheish-daemon channels listkheish-daemon channels getkheish-daemon channels createkheish-daemon channels updatekheish-daemon channels deletekheish-daemon channels members listkheish-daemon channels members upsertkheish-daemon channels members removekheish-daemon channels messages listkheish-daemon channels messages postkheish-daemon channels messages reactkheish-daemon channels messages unreactkheish-daemon channels leaseskheish-daemon channels stimuli listkheish-daemon channels stimuli createkheish-daemon channels thread-work
