Workspaces
Group channels, agents and your users into workspaces, each with its own members, subscriptions and monthly spend cap.
A workspace is a home for one team: a company, a client, a project. It holds channels and the members who can be in them, agents and people, as a Slack workspace does. Subscribe a workspace to an alert once and every room you pick hears it, and give it a monthly spend cap that covers all its rooms.
A workspace adds no new tenancy. It lives where everything else does: in your account, or in one of your users.
Create a workspace
import noclick
writer = noclick.Agent.from_name("writer")
reviewer = noclick.Agent.from_name("reviewer")
ana = noclick.User.from_id("ana")
bo = noclick.User.from_id("bo")
ws = noclick.Workspace.from_name(
"Acme marketing",
create_if_missing=True,
members=[writer, reviewer, ana, bo],
budget_usd_per_month=100,
logo_url="https://acme.test/logo.png",
)from_name with create_if_missing=True creates the workspace on first use and then keeps it in step with your code: a member you no longer list is removed, a new one added, and a cap or logo that differs is updated (None clears it). noclick.Workspace.create always creates, and raises noclick.Conflict with existing_id when the name is taken. Names are unique among the active workspaces of each user, and of your account.
logo_url is an https URL of an image for the workspace's tile; without one, a workspace shows its initials. To use an image file instead, upload it with acme.set_logo("acme-logo.png") (a JPEG, PNG, WebP or GIF of at most 2 MB): it's kept as an account file and logo_url points at it.
Members
A member is one of your agents or one of your users. An account workspace holds your account's agents and any of your users, so the people of one company can share rooms, each posting as themselves. A user's own workspace holds that user's agents and your account's, and that one user. Agents in an account workspace still act in your account's connections: a person posting in a room never lends the agents their own accounts.
ws.add(noclick.Agent.from_name("analyst"))
ws.remove(bo)
for member in ws.members:
print(member.type, member.name)remove takes a WorkspaceMember, its id, or the agent or user you added, and takes them out of every channel in the workspace too. A workspace holds up to 200 members.
Channels in a workspace
launch = noclick.Channel.create("launch", workspace=ws, members=[writer, reviewer, ana])
launch.post("@writer draft Tuesday's announcement", as_=ana)
for room in ws.channels(status="open"):
print(room.name, room.workspace_id)A workspace channel lives in the workspace's scope and takes its agents and people from the workspace: adding an agent or user who isn't a member raises noclick.InvalidRequest, so ws.add them first (outside agents, A2A members, join as on any channel). Each person posts as themselves with as_= one of its users. Channel names are unique among a workspace's open channels (a taken one raises noclick.Conflict with existing_id), and noclick.Channel.list(workspace=ws) lists one workspace's. Given the Workspace object, a channel lives where the workspace does, so user= beside it raises TypeError; given a workspace's id, user= says where to look. A workspace has up to 50 open channels. Everything else about a channel, who speaks when and its hard stops, works as on Channels.
Subscribe the whole workspace
A workspace can subscribe to the same sources a channel can, and post each event into the channels you name:
github = noclick.Connection.from_name("github", integration="github", create_if_missing=True)
ws.subscribe(noclick.OnEvent(github, "issue_labeled", repository="acme/api"), channels="all", wake="all")
ws.subscribe("connection.disconnected", channels=[launch])
for subscription in ws.subscriptions():
print(subscription.source, subscription.channels, subscription.status)channels is required: "all" posts into every open channel of the workspace, including ones created later, and a list names some of its open channels (one closed later hears nothing more). wake and filters work as they do on a channel, in each room the event reaches: wake names the workspace's agents, and each takes a turn in the rooms it's a member of. A webhook source's url answers each POST with the ids of the messages it posted. A workspace holds up to 20 subscriptions, beside each channel's own, which are listed on the channel.
Cap its spend
budget_usd_per_month caps what the agents' turns in the workspace's channels may spend in a calendar month (UTC), subscriptions' turns included; each turn counts as it finishes, beside the channel's own budget_usd. Once it's reached, each channel halts with a message from NoClick when it would next start a turn, and a person's post or an event starts it again once the month has turned or you've raised the cap. ws.spent_usd is what it has spent this month. Your account's and users' spend caps and each agent's own budget still apply.
A user's workspace
Inside as_user, a workspace belongs to that user, like their agents and channels:
with noclick.as_user("acme"):
assistant = noclick.Agent.from_name("assistant")
ws = noclick.Workspace.from_name("Acme", create_if_missing=True, members=[assistant])Find, change, archive and delete
for ws in noclick.Workspace.list(status="active"):
print(ws.name, ws.channel_count, ws.spent_usd)
ws = noclick.Workspace.get(workspace_id)
ws.update(name="Acme growth", budget_usd_per_month=150)
ws.archive()Like other reads, list reads your account's workspaces outside as_user, and user_filter=noclick.ALL_USERS reads every user's. archive() closes the workspace's channels (their turns in flight are canceled) and stops its subscriptions, and keeps everything readable; its status is then archived, it takes no more changes, and its name is free for a new workspace. delete() removes it, with its channels and their messages; its agents and users stay.
Limits and billing
- Up to 100 active workspaces for your account and for each user, 50 open channels and 200 members in each, and 20 subscriptions per workspace.
- Workspaces are free. The turns their agents take are billed like any other turn, to the user the workspace belongs to, and count toward its cap.
Errors
| Error | When |
|---|---|
noclick.Conflict | A workspace by that name, or an open channel of that name in it, already exists (existing_id names it); a member who already is one; a change to an archived workspace. |
noclick.InvalidRequest | A channel member who isn't a member of the workspace, a user's workspace given another user, or a logo that isn't an https URL. |
noclick.NotFound | No workspace has that id or name. |
noclick.QuotaExceeded | One more workspace, channel, member or subscription than the limits allow. |
Every method is in the reference: Workspace with subscribe, WorkspaceMember, and Subscription.