Skip to content

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:

  1. 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.
  2. 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_KEY set on hivemind-server — 32 bytes hex-encoded (64 hex chars), or HIVEMIND_INTEGRATION_ENCRYPTION_KEY_FILE pointing at a secret file. Without it, every integration route returns 503 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}/chatalot for historical reasons, but {id} is the agent profile id from step 1 (validated against agent_profiles).
  • provision_token is the bot:provision token — 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/communities with the same instance_url + provision_token.
  • expires_in_hours (optional, default 720 = 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 .
  • title 1–200 chars, content 1–4000 chars (split long material into several snippets), source optional provenance.
  • The first insert flips agent_profiles.knowledge_search_enabled = true for 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 with GET /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_mode yet — the profile update endpoint does not expose it. For now an operator sets it directly in the database:

UPDATE agent_profiles SET channel_reply_mode = 'both' WHERE slug = 'support-bot';

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:

  1. Decrypt + echo-guard. The frame is E2E-decrypted; messages from the bot itself are skipped.
  2. 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 / @channel and email-like name@host collisions do not count.
  3. 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.
  4. Conversation continuity. A stable conversation id is derived per (channel, sender), so multi-turn DMs stay coherent and users are isolated from each other.
  5. Limits. The profile's rate_limit_rpm / rate_limit_rpd and cost_cap_daily_cents apply 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, default true) controls REST cert verification for that integration. Set false only 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
HIVEMIND_CHATALOT_CA_BUNDLE=/path/to/chatalot-ca.pem

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.

  • HIVE-43 (EPIC) — chatalot bot integration (provisioning + outbound tools).
  • HIVE-105 — inbound grounded support bot (this guide's flagship flow); includes the search_bot_knowledge OR-match + rank-floor retrieval fix.
  • HIVE-106 — API/UI to set channel_reply_mode (channel @-mentions self-service).
  • CHAT-10 — Chatalot bot-identity primitive.