跳转到内容

Group Brain

此内容尚不支持你的语言。

Status: live in production since 2026-10-24, and on staging. Shadow-only: every decision is recorded and answered SILENT until Wetel staff switch your profile live.

The Group Brain API is for platforms that run their own chat network (their own WhatsApp, Telegram or web chat integration) and want a Wetel assistant in group chats. In a group, most messages are not for the assistant. You send each new batch of group messages to groupDecide; Wetel decides whether the assistant should speak, stay silent or wait, and keeps the per-group state (cooldown, daily cap, mute, talkativeness, who it is talking to). You keep the chat history and post the replies. If you only need an assistant that answers when mentioned, the Answer When Mentioned recipe works in production today with the standard SDK operations and no group-specific API.

  • A profile. A channel connector of type API in your workspace, linked to the agent that should answer. Its id is the profileId in every call. Its group settings (talkativeness, aliases, cooldown, daily cap, shadow) are set with updateChannelConnectorGroupMode, owner-only.
  • A named API key with the groups:decide scope, granted by the workspace owner or Wetel staff. Send it as X-Api-Key, with x-huat-platform: customer. A missing, unknown, revoked or expired key answers UNAUTHENTICATED; a valid key without the scope (or the legacy tenant key) answers FORBIDDEN; a profileId that is not an API connector in the key’s workspace answers NOT_FOUND.
  • Rate limits: 120 calls per minute per API key for groupDecide, and 120 per minute per key for each of the other three operations. Over the limit, the error carries extensions.status 429: back off and retry.
  1. Debounce new group messages on your side, then call groupDecide with the new messages (events, 1 to 20) and, for context, up to 20 earlier ones (recent, with your bot’s own messages marked fromBot: true).
  2. On SPEAK: send turnText as one user turn with sdkSendMessage on a session you keep for that group, post the reply threaded to replyToMessageId, then call groupReportOutcome. If notice is set, post it once in the group (before or with the first reply) and report NOTICE_POSTED.
  3. On SILENT: do nothing.
  4. On WAIT: if no new message arrives within retryAfterMs, call groupDecide once more with the same events, the same requestKey and waited: true. A second wait becomes SILENT.

Send one call per group at a time; Wetel also serialises calls per group.

mutation Decide($input: GroupDecideInput!) {
groupDecide(input: $input) {
decisionId
action
stage
reasonCode
turnText
replyToMessageId
retryAfterMs
notice
shadow
shadowAction
shadowReasonCode
replayed
}
}

GroupDecideInput: profileId, groupId (up to 189 letters, digits and @ . _ + -, no :), optional threadId (each thread is its own conversation), optional groupTitle and channel (for example whatsapp; defaults to api), requestKey (your id of the newest event), botSenderId, events, optional recent, optional waited.

Each event: kind (TEXT, or MEDIA_OTHER for a sticker, GIF, poll or attachment with no usable words), messageId, senderId (a stable id, never a display name), optional senderLabel (shown to the model only), sentAt, text (up to 4000 characters; put a voice-note transcript or photo caption here), mentionsBot, quotedFromBot, optional quotedSenderId and mentionedOthers. You detect mentions and replies; Wetel does not read your network.

Behaviour to rely on:

  • Synchronous. Usually under a second when rules decide (stage: RULES), a few seconds when a small judge model is asked (stage: JUDGE). The judge is given up after 6 seconds and the answer is SILENT.
  • Fail-closed. A kill switch, a profile that is off or paused, a judge failure, timeout or cost cap all answer SILENT with a reasonCode, never an error. With the kill switch on, decisionId is null.
  • Shadow. While shadow is true, action is SILENT with reasonCode: "shadow", and shadowAction/shadowReasonCode say what would have happened.
  • Idempotent. The same requestKey for the same group returns the stored decision with replayed: true and counts nothing twice. A retry that arrives while the first call is still being judged answers WAIT with reasonCode: "in_flight": ask again after retryAfterMs with the same requestKey and the same waited value.
  • Follow-up window. After the agent answers a person, that person’s next message within continuityMs (default 120000 ms, 0 to 3600000) is a follow-up candidate: the rules do not decide it, the judge does, with the hint that it may continue the conversation (features.followUpCandidate). The judge answers only if the message continues that conversation or is clearly meant for the agent; unrelated small talk gets SILENT (judge_silent). A follow-up still skips the rapid-exchange, cooldown and daily-cap rules, but in mention_only mode the window does not apply (mention_only). The reason code follow_up is kept for compatibility. With more than one agent in a group, give each distinct aliases: every agent that just answered the same person gets the follow-up candidate and decides for itself.
  • No message text stored. Wetel builds turnText from your input and does not store it; the decision log holds ids, codes and numbers only.

reasonCode values: speaking mention, quote_reply_to_bot, alias_match, follow_up, judge_reply; silent muted, own_message, addressed_other_human, rapid_human_exchange, media_only, cooldown, daily_cap, mention_only, mention_flood, stale_event, judge_silent, judge_below_threshold, judge_react, judge_error, judge_budget, judge_daily_cap, shadow, disabled, profile_off; wait judge_wait, in_flight.

groupReportOutcome(input: { decisionId, status, outboundMessageId }) returns true. status is SENT (you posted the reply), DROPPED_STALE (newer messages arrived, you did not post), FAILED, or NOTICE_POSTED (stops notice being returned for that group). It is accepted only for a live SPEAK decision: for a shadow or silent decision it answers BAD_REQUEST. Calling it again overwrites the status. It does not change cooldowns.

Applies a group admin command and returns the group’s state (muted, talkativeness, noticePosted, unpromptedToday, lastUnpromptedAt). Input: profileId, groupId, optional threadId, and any of muted, talkativeness (MENTION_ONLY, QUIET, NORMAL, CHATTY), clearTalkativeness, reset, plus actorId. Fields you omit are kept. You check that actorId is an admin of the group; Wetel records it. Muted means silent even when mentioned. Never forward admin commands to the agent as messages.

groupDecisions(filter: { profileId, since, groupId, threadId, first, after }) lists a profile’s recorded decisions since since, oldest first (Relay paging, first 1 to 200, default 100). Each record has ids (conversationId, requestKey, triggerMessageId, batchMessageIds), stage, action (in shadow, what it would have been), reasonCode, confidence, threshold, shadow, wouldSend, sent, languageBucket, latencyMs and outcomeStatus, never message text. Join them to your own history to label and evaluate shadow decisions before asking for go-live.