Guides
Channels, forums, threads, and reactions
Membership-based channels and forums, nested threads via threadRootId, structured mentions, and idempotent reactions.
GozWire keeps conversation access membership-based. Public discovery, ranking, votes, workspace provisioning, and product chrome are application concerns.
Channels and forums
await gozwire.conversations.createChannel({
creatorId: "user-a",
title: "engineering",
userIds: ["user-b"],
});
await gozwire.conversations.createForum({
creatorId: "user-a",
title: "ask",
userIds: ["user-b"],
});
Both kinds reuse the same participant roles as groups. Direct chats still cannot add participants. Outsiders cannot send, list history, or subscribe.
A blocked pair cannot be invited into a new group, channel, or forum. Errors match a missing identity. Members who already share a room may still send ordinary messages and thread replies.
Default retention for channel and forum is persistent only, with sender override off. A 24h or after-read send is rejected until the host expands retention.byType or an authorized participant updates conversation policy.
Nested threads
A top-level message is its own threadRootId. A reply copies the parentβs root, so nested replies stay in one tree.
messages.history({ rootsOnly: true })lists posts (thread_root_id = id).messages.thread({ threadRootId })pages that tree with cursors. There is no SQL OFFSET.
Cross-conversation replyToId values are rejected. Invisible (held or blocked) messages cannot be replied to. A blocked pair in a shared room may still use replyToId on a visible message.
Mentions
Mentions are structured identity IDs in metadata.mentions, not a body-text parse. Autocomplete must call conversations.membersForMention for the current conversation. That list is active members excluding the actor. Persist still drops blocked IDs. See messaging.
Reactions
await gozwire.reactions.add({ messageId, userId, reaction: "π" });
await gozwire.reactions.remove({ messageId, userId, reaction: "π" });
await gozwire.reactions.list({ messageId, actorId });
Reactions are stored as (message_id, user_id, reaction). Duplicate adds return the original row. The engine accepts any valid bounded emoji or :custom: token. config.reactions.suggested is picker inventory, not an allowlist. It defaults to ["π","β€οΈ","π"]. The only runtime editor is configuration.updateReactions({ suggested }).
What stays in the application
- Reddit-style ranking, awards, and feeds
- Discord or Slack channel directories and permissions beyond membership
- Account signup, SSO, and profile bios
- Custom emoji asset hosting