Chatalot integration — grounded support bot + agent tools
Connect a Hivemind agent to a Chatalot community as a first-class bot. Two capabilities, both under one bot identity Hivemind provisions and manages:
- Inbound grounded support bot — the bot listens on your community and answers questions, in DMs and (optionally) when @-mentioned in channels. Replies are grounded in a knowledge base you load, so it answers from your facts and says "I don't know" rather than inventing.
- Outbound agent tools — an agent can post messages, create channels, and mint invites under its bot identity (the agent tool-call surface).
This is the path AIWF and other design partners use to stand up a self-service
support bot. Everything below is the current shipped flow (no admin JWT — the
bot is provisioned with a least-privilege bot:provision token).
Prerequisites
- A Chatalot instance recent enough to support
bot:provision-scoped tokens and the bot-identity admin primitives Hivemind calls (/api/admin/bots,/api/admin/bots/{id}/tokens, community join, and the E2E sender-key APIs the bot uses to receive channel messages). HIVEMIND_INTEGRATION_ENCRYPTION_KEYset on hivemind-server — 32 bytes hex-encoded (64 hex chars), orHIVEMIND_INTEGRATION_ENCRYPTION_KEY_FILEpointing at a secret file. Without it, every integration route returns503 integration_key_missing. This key encrypts the bot's stored token at rest (ChaCha20-Poly1305).- A
bot:provision-scoped Chatalot token (cb_…) for the target instance, generated in Chatalot's admin UI. It is not an admin JWT: it can only provision a bot and mint that bot's token. Hivemind uses it once at connect time (sent in the request body, never a header you persist) and never stores it. - For a self-hosted Chatalot with a self-signed / private-CA cert, see TLS for self-hosted instances.
Authentication to Hivemind's own API: send X-API-Key: <admin key> (headless /
scripted), or use a browser session against the /api/admin/* mirror of each
route. If your instance is behind SSO forward-auth, see
api-reference.md
for the supported service-to-service access patterns.
End-to-end: stand up a grounded support bot
Five steps. $HIVE is your Hivemind base URL, $KEY your admin API key.
1. Create the agent profile
The bot's behavior lives in an agent profile (its system prompt, model, limits). Create one:
curl -fsS -X POST -H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
-d '{
"slug": "support-bot",
"name": "Support Bot",
"security_tier": 1,
"system_prompt": "You are the support agent for Acme. Answer from the supplied knowledge. If the answer is not in it, say so.",
"model": "claude-haiku-4-5-20251001",
"max_tokens": 1024,
"rate_limit_rpm": 60,
"rate_limit_rpd": 1000,
"cost_cap_daily_cents": 5000
}' \
"$HIVE/api/v1/agent-chat/profiles" | jq .
Capture the returned profile id (a UUID) — call it $PROFILE_ID. It is
the same id used for the chatalot connect call and the knowledge base below.
(Profiles are also listed at GET /api/v1/agent-chat/profiles and updated at
PUT /api/v1/agent-chat/profiles/{slug}.)
2. Connect the bot to Chatalot
This provisions a bot user in Chatalot, mints a long-lived bot token, encrypts it at rest, and (optionally) joins a community so the bot is discoverable.
curl -fsS -X POST -H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
-d '{
"instance_url": "https://chat.example.com",
"provision_token": "cb_<your bot:provision token>",
"bot_username": "support-bot",
"display_name": "Support",
"default_community_id": "<community-uuid>",
"default_community_role": "member"
}' \
"$HIVE/api/v1/agents/$PROFILE_ID/chatalot/connect" | jq .
Notes:
- The path segment is
/agents/{id}/chatalotfor historical reasons, but{id}is the agent profile id from step 1 (validated againstagent_profiles). provision_tokenis thebot:provisiontoken — used once, never stored.default_community_id(optional) auto-joins the bot to a community on connect. Chatalot scopes user search to community peers, so a bot in zero communities is invisible to non-owners — join at least one. To list joinable communities first (e.g. to populate a picker):POST /api/v1/agents/$PROFILE_ID/chatalot/communitieswith the sameinstance_url+provision_token.expires_in_hours(optional, default720= 30 days) sets the bot token lifetime.- On success (
201) the connect call also provisions the bot's E2E crypto identity and sender keys for member channels, so it can receive @-mentions.
A 201 returns the IntegrationStatus (instance_url, bot_user_id,
bot_username, token_prefix, status: "active", …).
3. Load the knowledge base
Grounding is opt-in and turns on automatically the first time you add a snippet. Add one row per fact/snippet:
curl -fsS -X POST -H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
-d '{
"title": "Support hours",
"content": "Support is staffed Tue & Thu, 10am-6pm Mountain.",
"source": "manual"
}' \
"$HIVE/api/v1/bots/$PROFILE_ID/knowledge" | jq .
title1–200 chars,content1–4000 chars (split long material into several snippets),sourceoptional provenance.- The first insert flips
agent_profiles.knowledge_search_enabled = truefor this profile; from then on every reply is grounded. - Manage with
GET /api/v1/bots/$PROFILE_ID/knowledge(list),PATCH …/knowledge/{id}(update),DELETE …/knowledge/{id}(remove), and preview retrieval withGET /api/v1/bots/$PROFILE_ID/knowledge/search?q=<query>.
4. Enable channel @-mention replies (optional)
By default a bot replies in DMs only (channel_reply_mode = "dm_only").
To have it also answer when @-mentioned in channels, set the profile's
channel_reply_mode:
| Value | Behavior |
|---|---|
dm_only (default) |
Replies in DMs; ignores channels |
channel_mention |
Replies only when @-mentioned in a channel; ignores DMs |
both |
Replies in DMs and when @-mentioned in channels |
Current limitation (HIVE-106): there is no self-service API to set
channel_reply_modeyet — the profile update endpoint does not expose it. For now an operator sets it directly in the database:An API/UI control for this is tracked in HIVE-106.
5. The bot answers — automatically
No further wiring. Hivemind runs a background bot-WebSocket supervisor that polls for active integrations (every ~5 minutes), opens a persistent encrypted WebSocket to Chatalot per bot, and routes inbound DMs and channel @-mentions through the grounded answer path. Send the bot a DM, or @-mention it in a channel it has joined, and it replies grounded in your knowledge.
How inbound answering works (reference)
When a message arrives on the bot's WebSocket:
- Decrypt + echo-guard. The frame is E2E-decrypted; messages from the bot itself are skipped.
- Reply-surface gate (
channel_reply_mode). DMs vs channels per the table above. In a channel, the bot replies only if the message actually @-mentions it by username —@everyone/@here/@channeland email-likename@hostcollisions do not count. - Grounded answer. The message runs through the agent profile's chat path.
If
knowledge_search_enabled, Hivemind retrieves the top matching knowledge snippets and injects them into the system prompt under a "Knowledge about the operator's organization" header that instructs the model to answer from them or admit it doesn't know. - Conversation continuity. A stable conversation id is derived per (channel, sender), so multi-turn DMs stay coherent and users are isolated from each other.
- Limits. The profile's
rate_limit_rpm/rate_limit_rpdandcost_cap_daily_centsapply per Chatalot user. When a limit trips, the bot sends a short canned reply ("I'm getting too many messages right now…" / "I'm temporarily limited for today…") rather than going silent. Internal errors are dropped silently (no spam).
Knowledge retrieval (grounding)
Knowledge lives in the bot_knowledge table (Postgres full-text search,
title weighted above content). Retrieval lexes the user's message and
OR-matches the terms with a relevance floor, so realistic conversational
questions (with filler words or an @-mention token) still match — this is the
HIVE-105 fix; an earlier AND-only match meant grounding almost never fired.
The top hits (up to 5) are injected into the prompt; if nothing clears the
relevance floor, the bot answers from its base prompt and is told to say it
doesn't know rather than guess. If retrieval errors, the chat degrades
gracefully (answers without grounding) instead of failing.
Connecting the bot (lifecycle reference)
All lifecycle routes are mounted at /api/v1/agents/{profile_id}/chatalot
(API-key auth) and mirrored at /api/admin/agents/{profile_id}/chatalot
(browser-session auth). Same handlers; auth differs.
| Route | Purpose |
|---|---|
POST /connect |
Provision the bot + persist the encrypted token (step 2). |
POST /communities |
List joinable communities (for a default-community picker). Body: instance_url, provision_token, optional verify_tls. |
POST /test |
Sanity-probe the stored token against Chatalot; flips status to revoked_externally on a 401. |
GET / (no suffix) |
Return IntegrationStatus (metadata only; no Chatalot call). 404 not_configured if none. |
DELETE / |
Drop the local integration row. The Chatalot-side token lingers until expiry — revoke it in Chatalot for active revocation. |
TLS for self-hosted instances
verify_tls(connect body, defaulttrue) controls REST cert verification for that integration. Setfalseonly for a self-signed / private-CA instance — it drops MITM protection for that instance (logged at WARN) and applies to REST calls only.- The bot's WebSocket connector always full-verifies the chain and hostname — there is no per-integration opt-out. For a self-signed / private-CA Chatalot where the bot uses the WebSocket (the inbound path always does), set the server-level
pointing at the leaf cert (purely self-signed) or its private CA. Both the REST and WS clients pick those roots up additively, on top of the system trust store, with full chain verification intact. This is the preferred path for production self-hosted Chatalot.
Token-revocation recovery
If Chatalot returns 401 for the bot token (e.g. revoked in Chatalot), Hivemind
flips the integration to status = 'revoked_externally' and short-circuits
further calls with 409 integration_disabled (so one 401 doesn't stampede).
To recover, run POST /connect again with a fresh bot:provision token; status
resets to active.
Outbound agent tools (optional)
Independently of the support-bot path, an agent can act in Chatalot via a tool surface — useful for an agent that posts digests, opens channels, or invites members.
POST /api/v1/agents/{profile_id}/chatalot/tools/{tool_name}
The path's tool_name MUST match the body's tool discriminator (mismatch →
400 tool_name_mismatch).
| Tool | Inputs | Notes |
|---|---|---|
send_message |
channel_id, content |
Posts via a per-channel webhook (Chatalot has no REST message-send). The bot auto-creates + caches the webhook on first send; this needs the bot to hold a community owner/admin role (else Chatalot returns 403). Renders as a webhook message (plaintext); Chatalot rate-limits to 1 msg/sec per webhook. |
create_channel |
group_id, name, optional kind (default text), description |
Create a channel in a group. |
invite_member |
community_id, optional email, role, expires_at |
Mint an invite; without email, a generic invite link. |
Response:
{ "ok": true, "tool": "send_message", "target_id": "<uuid>", "status": 200, "payload": { /* chatalot response */ }, "error_msg": null }
ok=false with a 4xx/5xx mirrors Chatalot's status so callers can branch; a
401 flips the integration to revoked_externally and later calls return
409 integration_disabled until you reconnect. Every call writes an
agent_audit_log row (event_type=chatalot_tool_call, severity info on 2xx /
warning otherwise).
Agent runtime tool-call loop
When a profile has can_use_tools = true and the server has
HIVEMIND_INTEGRATION_ENCRYPTION_KEY set, a chat against that profile exposes
the tools to the model as chatalot_send_message, chatalot_create_channel,
and chatalot_invite_member, looping assistant ↔ tool up to a hard
MAX_ITERATIONS = 10. Each Claude call counts against the profile's
cost_cap_daily_cents; the loop writes a tool_loop_completed audit row
(tool_calls=N cost_cents=X hit_cap=bool) so operators can spot runaway loops.
Operator UI
The Hivemind dashboard's AI Agents page (/ai-agents) shows each profile's
integration status as a pill (gray not configured, green connected with
token prefix, red revoked_externally). Manage opens a modal whose Connect
form takes instance_url, the bot:provision token, bot_username,
display_name, and optional default-community / expiry — submitting hits
POST /api/admin/agents/{profile_id}/chatalot/connect. Test and
Revoke+disconnect are one-click. The digest auto-refreshes every 30s.
Limitations / out of scope
- Self-service
channel_reply_mode— no API/UI yet; set via SQL (above). Tracked: HIVE-106. - In-product knowledge ingestion UI — load via the API today; a bulk/URL ingestion UI is a separate track.
- Multi-instance bots — one agent profile connects to one Chatalot instance per integration.
- Bot-token rotation without re-provisioning — to rotate, revoke in
Chatalot and re-run
/connect. - Chatalot-side per-bot-token rate limits — Hivemind's per-profile limits apply, but Chatalot does not currently rate-limit bot-token requests itself.
Related tickets
HIVE-43(EPIC) — chatalot bot integration (provisioning + outbound tools).HIVE-105— inbound grounded support bot (this guide's flagship flow); includes thesearch_bot_knowledgeOR-match + rank-floor retrieval fix.HIVE-106— API/UI to setchannel_reply_mode(channel @-mentions self-service).CHAT-10— Chatalot bot-identity primitive.