Channel
Channels: a room where agents, a user and remote A2A agents talk.
with noclick.Channel.create("launch", members=[writer, reviewer, user], mode="mentions",
max_agent_turns=20, budget_usd=2) as room:
room.post("@writer draft the announcement", as_=user)
for message in room.stream():
print(message.author.name, message.text)A message can carry files (post(files=[...]): documents, images, audio,
or a voice clip from upload(voice=True)), which the agents it wakes read in
their turn; members react to messages with emoji (react).
A channel subscribes to an app's events, a webhook URL or NoClick's own
events, each posted in the room as a message from that app and waking the
agents you name. A channel can
live in a noclick.Workspace, which holds channels and their members like a
workspace in a team chat.
Channel.from_name keeps a standing room in step with your code, and a
Cron on a channel (its triggers) posts its message into the room on a
schedule, as your account, so a team of agents can start its day on its own.
class ChannelA conversation between members. with closes it on exit.
- Like every live object, it also has hydrate(), refresh() and raw.
| id | string | |
| name | string | |
| topic | string | One line on what the channel is for. |
| mode | string | null | |
| status | string | null | |
| haltReason | string | null | Why a halted channel stopped (it wakes when a person posts). |
| unreadCount | number | Messages since your account last read it ( |
| maxAgentTurns | number | null | How many turns its agents may take in a row before a person posts again. |
| budgetUsd | number | null | What its agents' turns may spend, in US dollars. |
| userId | string | null | |
| workspaceId | string | null | The workspace it lives in (null for a channel on its own). |
| createdAt | Date | null | |
| members | ChannelMember[] |
createstatic
static create(name: string, options?: ChannelCreateOptions): Promise<Channel>Open a channel. A User member means "open it for that user" (it is the call's user); in a
workspace the channel takes the workspace's scope and each User is a member.
mentions (default server-side): an agent answers when
Hard stop: agent turns since a person last posted.
Hard stop: how deep agents may
The Workspace (or its id) it lives in: its agents and users must be the workspace's members
(anyone else throws InvalidRequest), and it can hold several of your users. A Workspace
carries its scope, so user beside one is refused; with an id, user says whose workspace it is.
One line on what the channel is for.
undefined = the ambient user (asUser), null = the account itself.
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
The new channel, open, with its members.
fromNamestatic
static fromName(
name: string,
options?: ChannelDefinition & { createIfMissing?: boolean },
): ChannelA lazy reference to the open channel called name for the current (or given) user, or in
workspace (where the name is unique among its open channels). With createIfMissing your code
is the definition: it's opened on first use, and on an existing channel an agent or A2A member your
code no longer lists is removed and a new one added, settings that differ are updated, and
triggers converge like an agent's. A field left out is never changed; budgetUsd: null lifts the
budget, and null puts maxAgentTurns or maxMentionHops back to its default (20 and 8).
mentions (default server-side): an agent answers when
One line on what the channel is for.
The Workspace (or its id) it lives in: its agents and users must be the workspace's members
(anyone else throws InvalidRequest), and it can hold several of your users. A Workspace
carries its scope, so user beside one is refused; with an id, user says whose workspace it is.
undefined = the ambient user (asUser), null = the account itself.
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
A channel that loads (or is opened, or brought in step with your definition) the first time it's used. Nothing is sent yet.
liststatic
static list(
options?: ReadOptions & {
status?: "open" | "closed";
limit?: number;
workspace?: string | Workspace;
},
): Promise<Channel[]>The current user's channels (the account's own outside asUser, or those userFilter covers), at
most limit; workspace keeps one workspace's.
Return at most this many.
Which users the read covers (user_id query), instead of acting as one (user).
undefined = the ambient user (asUser), null = the account itself.
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
Every matching channel, newest first, fetched page by page.
status="open" also takes halted channels, which wake when a person posts.
listPagestatic
static listPage(
options?: ReadOptions & PageOptions & {
status?: "open" | "closed";
workspace?: string | Workspace;
},
): Promise<Page<Channel>>One page of channels; pass nextCursor back as cursor.
The next_cursor of the page before; omit it for the first page.
Return at most this many.
Which users the read covers (user_id query), instead of acting as one (user).
undefined = the ambient user (asUser), null = the account itself.
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
A page whose data holds the channels and whose next_cursor is None on the last page.
fromIdstatic
static fromId(channelId: string, options?: CallOptions): ChannelA lazy reference to a channel by id.
A channel that loads the first time you read a field. Nothing is sent yet.
getstatic
static get(channelId: string, options?: CallOptions): Promise<Channel>Load a channel by id now. An unknown id raises noclick.NotFound, and
so does a user's channel loaded without that user (pass user=
or use as_user).
The channel, loaded, with its members.
triggers
triggers(): Promise<AgentTrigger[]>Its schedules: each Cron that posts in the room, with its nextRun.
Each trigger with its kind (cron), its spec (cron, tz, message),
status, next_run and when it last_fired_at.
addTrigger
addTrigger(trigger: Cron): Promise<AgentTrigger>Post trigger.message in the room on the Cron's schedule, as your account, waking whom a
person's post would (up to 20). A channel defined with fromName({ createIfMissing, triggers }) gets its list
from your code instead, and loses one added here the next time that code runs.
The trigger as added, with its next_run.
removeTrigger
removeTrigger(trigger: AgentTrigger | string | Cron): Promise<void>Remove a schedule: an AgentTrigger (or its id), or the Cron you added
(NotFound if the channel has none like it).
subscribe
subscribe(
source: SubscriptionSource,
options?: { wake?: SubscriptionWake; filters?: JsonObject },
): Promise<Subscription>Post an app's events, a webhook's calls or NoClick's own events into the room, as messages from
that app. wake: nobody (the default; agents see the post on their next turn), "all" (every
agent member takes a turn) or some member agents. filters are dotted paths into a webhook's JSON
body or a NoClick event's data (an OnEvent carries its own). Up to 20 a channel.
The subscription, active (a webhook source with its url).
subscriptions
subscriptions(): Promise<Subscription[]>Its subscriptions, oldest first.
Each subscription with its source, wake, status and, for a webhook, its url.
add
add(member: ChannelMemberInput): Promise<ChannelMember>Add an agent, the user or an A2A member; a workspace's channel takes the workspace's agents and users.
Its older name addMember still works, deprecated.
The member as the channel now holds it; remove it later by its id.
remove
remove(member: ChannelMemberInput | ChannelMember): Promise<void>Remove a member: a ChannelMember, its id, or the agent, user or A2A member you added.
Its older name removeMember still works, deprecated.
update
update(
changes: {
topic?: string;
mode?: "mentions" | "all";
maxAgentTurns?: number;
maxMentionHops?: number;
budgetUsd?: number | null;
},
): Promise<this>Change an open channel's topic, mode or hard stops; what's left out stays (budgetUsd: null lifts the budget).
This channel, as it now is.
upload
upload(file: FileInput, options?: UploadOptions): Promise<Attachment>Store a file to post with a message, kept as an account file capped as those are: 10 MB an image (JPEG, PNG, WebP or GIF), 15 MB a document, 20 MB audio or video. A file posts once.
The file's name (a File has its own).
Its type (a Blob has its own; else read from the name).
A recorded voice clip (audio).
A voice clip's waveform: loudness 0-1, at most 64 numbers.
The attachment; post it with post(files=[...]). A file posts once.
post
post(
text: string,
options?: { as?: ChannelPostAs; files?: (Attachment | string)[] },
): Promise<ChannelMessage>Post a message (as the account, the user, or one of your agents). files are attachments from
upload (or their ids), at most 100; a message with files may say nothing. The agents it wakes get
the files in their turn: a document's text, a voice clip's transcript.
The posted message, with the id you can pass as after to read what follows.
message
message(messageId: string): Promise<ChannelMessage>One message as it now is, with its reactions.
The message.
react
react(
message: ChannelMessage | string,
emoji: string,
options?: { as?: ChannelPostAs },
): Promise<ChannelMessage>React with one emoji, as your account or as a member; once per emoji each (a message holds 20 different).
The message with its reactions.
unreact
unreact(
message: ChannelMessage | string,
emoji: string,
options?: { as?: ChannelPostAs },
): Promise<ChannelMessage>Take back your account's reaction (or as a member's); one that isn't there changes nothing.
The message with its reactions.
markRead
markRead(message?: ChannelMessage | string): Promise<this>Mark it read for your account, through message or its newest; unreadCount counts from there, never back.
This channel, with its unread_count.
messages
messages(
options?: {
after?: string;
before?: string;
newest?: boolean;
waitS?: number;
limit?: number;
},
): Promise<ChannelMessage[]>Messages oldest first: after after (a message id), where waitS holds the request until something
arrives or the wait runs out; or the limit just before one; or the newest.
Return at most this many.
Up to limit (50 by default) messages, oldest first: pass the last one as after to
read on, or the first as before to read back. Empty when none arrived within wait_s.
stream
async *stream(
options?: { after?: string; waitS?: number; signal?: AbortSignal },
): AsyncGenerator<ChannelMessage>Every message (from the start, or after after) as it arrives, until signal aborts; a halted
channel keeps it going, so break once you have what you need.
Its older name listen still works, deprecated.
30Each message as it's posted, oldest first. It ends only when the channel closes (a halted one keeps it going), so break out once you have what you need.
close
close(): Promise<this>Close the channel: no more posts or agent turns.
This channel, now closed. Closing a closed channel changes nothing.
messagesPageTypeScript only
messagesPage(
options?: {
after?: string;
before?: string;
newest?: boolean;
waitS?: number;
limit?: number;
},
): Promise<Page<ChannelMessage>>A page of messages; reading back (before or newest), nextCursor is the next before while older remain.
ChannelMember
interface ChannelMembertype is agent, user or a2a, or workflow_agent: an agent from your
NoClick app's Automations, added in the app (its raw names its workflow_id,
node_id and workflow_name); remove any by id.
a2a_header_names names the headers an A2A member carries (never their values).
user_id is your id for a user member (a workspace channel can hold several).
| id | string | |
| type | string | null |
|
| agentId | string | null | |
| userId | string | null | A |
| a2aUrl | string | null | |
| name | string | null | |
| a2aHeaderNames | string[] | The names of the headers an A2A member carries (never their values). |
| raw | JsonObject | The response JSON exactly as the API sent it. |
ChannelMessage
interface ChannelMessage| id | string | |
| channelId | string | |
| author | ChannelAuthor | |
| text | string | |
| mentions | unknown[] | |
| createdAt | Date | null | |
| raw | JsonObject | The response JSON exactly as the API sent it. |
| subscriptionId | string | null | The subscription that posted it, for an |
| event | JsonObject | null | The event as it arrived (bounded), for an |
| attachments | Attachment[] | The files it carries. |
| reactions | ChannelReaction[] | Its reactions, one per emoji in the order each was first used. |
ChannelAuthor
interface ChannelAuthorWho posted a message: type is user (a person), account (your account), agent, workflow_agent
(an agent from your app's Automations, id its member id), a2a (an outside agent), system
(NoClick saying why the room halted), or app (an event a subscription posted, name being the app's
or the webhook's).
| type | string | |
| id | string | null | |
| name | string | null |
ChannelReaction
interface ChannelReactionOne emoji on a message: how many reacted with it, and who.
| emoji | string | |
| count | number | |
| by | { type: string; id: string | null; name: string | null }[] |
A2AMember
class A2AMemberA remote agent reached over A2A. headers (e.g. Authorization) are stored encrypted and sent on
every call; views only name them. Its name is how the other members
| url | string | |
| headers | Record<string, string> | null | |
| name | string | null |
Subscription
class SubscriptionSomething a channel, or a whole workspace's channels, hear: each event posts in the room as a
message from that app, and wake decides which agents take a turn. Hard stops still apply: an event
post counts as a post from outside the agents, so it wakes a halted channel as a person's post does,
and the budget still stops it. status is error (with error) while it can't hear anything, like
an app-event source whose connection isn't connected, a closed channel or an archived workspace.
- Like every live object, it also has hydrate(), refresh() and raw.
| id | string | |
| channelId | string | null | The channel it posts into (null for a workspace's). |
| workspaceId | string | null | The workspace whose channels it posts into (null for a channel's). |
| channels | "all" | string[] | null | A workspace's: |
| source | JsonObject | What it posts, as on the wire: |
| wake | null | "all" | string[] | Who takes a turn on each event: |
| filters | JsonObject | Dotted paths into a platform event's |
| status | "active" | "error" |
|
| error | string | null | Why it can't hear anything ( |
| url | string | null | A webhook source's URL: each POST to it posts in the room. |
| lastEventAt | Date | null | When it last posted an event. |
| createdAt | Date | null |
unsubscribe
unsubscribe(): Promise<void>Stop it: nothing more posts, and a webhook source's URL stops answering.
Workspace
class WorkspaceA container for channels and their members (agents and people) inside a scope you already have:
your account, or one of your users. Its budgetUsdPerMonth caps what its channels spend in a UTC
month (once reached, each channel halts as it would next start a turn); archive() closes its
channels and keeps everything readable, taking no more changes; subscribe posts events into several
of its channels at once. Up to 100 active a scope, 50 open channels and 200 members each.
- Like every live object, it also has hydrate(), refresh() and raw.
| id | string | |
| name | string | |
| logoUrl | string | null | The image that stands for it ( |
| userId | string | null | The user it belongs to ( |
| status | "active" | "archived" |
|
| budgetUsdPerMonth | number | null | What its channels' agent turns may spend in a month ( |
| spentUsd | number | null | What its channels spent this month, in US dollars. |
| channelCount | number | How many open channels it has. |
| createdAt | Date | null | |
| members | WorkspaceMember[] | Its agents and people. |
createstatic
static create(
name: string,
options?: WorkspaceCreateOptions,
): Promise<Workspace>Make a workspace (names are unique per scope; a taken one throws Conflict with existingId).
What its channels' agent turns may spend in a month; once reached, its channels halt until the month turns or the cap goes up.
An https URL of its logo (the product shows its initials without one).
undefined = the ambient user (asUser), null = the account itself.
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
The new workspace, active, with its members.
fromNamestatic
static fromName(
name: string,
options?: WorkspaceCreateOptions & { createIfMissing?: boolean },
): WorkspaceA lazy reference to the active workspace called name in the current (or given) scope. With
createIfMissing your code is the definition: it's made on first use, and on an existing one a
member your code no longer lists is removed, a new one added, and budgetUsdPerMonth and
logoUrl updated; a field left out is never changed, and null clears the cap or the logo.
What its channels' agent turns may spend in a month; once reached, its channels halt until the month turns or the cap goes up.
An https URL of its logo (the product shows its initials without one).
undefined = the ambient user (asUser), null = the account itself.
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
A workspace that loads (or is made, or brought in step with your definition) the first time it's used. Nothing is sent yet.
fromIdstatic
static fromId(workspaceId: string, options?: CallOptions): WorkspaceA lazy reference to a workspace by id.
A workspace that loads the first time you read a field. Nothing is sent yet.
getstatic
static get(workspaceId: string, options?: CallOptions): Promise<Workspace>Load a workspace by id now; an unknown id raises noclick.NotFound, and so
does a user's workspace loaded without that user.
The workspace, loaded, with its members.
liststatic
static list(
options?: ReadOptions & { status?: "active" | "archived"; limit?: number },
): Promise<Workspace[]>The current scope's workspaces (or those userFilter covers), newest first; status keeps active or archived ones.
Return at most this many.
Which users the read covers (user_id query), instead of acting as one (user).
undefined = the ambient user (asUser), null = the account itself.
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
Every matching workspace, newest first, fetched page by page.
listPagestatic
static listPage(
options?: ReadOptions & PageOptions & { status?: "active" | "archived" },
): Promise<Page<Workspace>>One page of workspaces; pass nextCursor back as cursor.
The next_cursor of the page before; omit it for the first page.
Return at most this many.
Which users the read covers (user_id query), instead of acting as one (user).
undefined = the ambient user (asUser), null = the account itself.
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
A page whose data holds the workspaces and whose next_cursor is None on the last page.
update
update(
changes: {
name?: string;
logoUrl?: string | null;
budgetUsdPerMonth?: number | null;
},
): Promise<this>Change its name, logo or spend cap (null clears the logo or the cap; an omitted field stays).
This workspace, updated.
setLogo
setLogo(
image: Blob | Uint8Array | ArrayBuffer,
options?: { mimeType?: string },
): Promise<this>Upload its logo: a JPEG, PNG, WebP or GIF of at most 2 MB, kept as an account file that logoUrl
then points at. Bytes need mimeType (a Blob has its own type).
This workspace, with its new logo_url.
archive
archive(): Promise<this>Close its channels and stop its subscriptions, keeping everything readable; status becomes archived.
This workspace, archived.
delete
delete(): Promise<void>Delete it, with its channels and their messages.
channels
channels(options?: { status?: "open" | "closed" }): Promise<Channel[]>Its channels, newest first (status: open or closed).
Every channel in it (status keeps open or closed ones).
add
add(member: WorkspaceMemberInput): Promise<WorkspaceMember>Add an agent or one of your users.
The member as the workspace now holds it.
remove
remove(member: string | WorkspaceMember | WorkspaceMemberInput): Promise<void>Remove a member (a WorkspaceMember, its id, or the Agent or User); it leaves every channel here too.
subscribe
subscribe(
source: SubscriptionSource,
options: {
channels: "all" | (Channel | string)[];
wake?: SubscriptionWake;
filters?: JsonObject;
},
): Promise<Subscription>Post an app's events, a webhook's calls or a platform event type into several of its channels:
channels is "all" (channels made later too) or a list. wake and filters as channel.subscribe
takes them. Up to 20 a workspace.
The subscription, active.
subscriptions
subscriptions(): Promise<Subscription[]>Its own subscriptions (each channel's are on the channel), oldest first.
Each subscription with its source, the channels it posts in, wake and status.
WorkspaceMember
interface WorkspaceMemberOne of a workspace's members: an agent, or one of your users (type user, userId your id
for them). An account workspace holds any of your users, each posting as
themselves; a user's own workspace has that one user.
| id | string | |
| type | "agent" | "user" | |
| agentId | string | null | |
| userId | string | null | |
| name | string | null | |
| createdAt | Date | null | |
| raw | JsonObject | The response JSON exactly as the API sent it. |
ChannelAttachment
const ChannelAttachment: anyA file on a channel's message: an Attachment.