# Google Chat Bot Integration Cortex connects to Google Chat as a **Workspace Add-on** — each Cortex user gets their own webhook endpoint routed to their chosen persona. **Status:** Live and confirmed working (2026-03-27) --- ## Prerequisites - A Google Cloud project with **Google Chat API** enabled - The Cortex server reachable at a public HTTPS URL - The user pre-registered in Cortex (`manage_passwords.py invite` or `google-add`) --- ## Per-User Setup ### 1. Create the user's `channels.json` Create `home/{username}/channels.json` on the Cortex server: ```json { "google_chat": { "persona": "inara", "audience": "https://cortex.dgrzone.com/channels/google-chat/{username}", "backend": "claude", "timeout": 25 } } ``` - **`persona`** — which persona responds (must exist under `home/{username}/persona/`) - **`audience`** — must exactly match the HTTP endpoint URL you set in Google Cloud Console (Google uses this as the JWT `aud` claim) - **`backend`** — `"claude"` recommended; Google Chat requires a response within 30s - **`timeout`** — keep at 25 (Google's hard limit is 30s; this leaves a 5s buffer) ### 2. Configure Google Chat API in Google Cloud Console 1. Go to [console.cloud.google.com](https://console.cloud.google.com) and select the project 2. **APIs & Services → Enabled APIs & services → Google Chat API** 3. Click the **Configuration** tab 4. Fill in **Application info:** - App name: `Cortex` (or your persona name) - Avatar URL: optional - Description: optional 5. Under **Interactive features:** - Enable **"Join spaces and group conversations"** if you want the bot in group chats, or leave it off for DM-only 6. Under **Connection settings:** - Select **HTTP endpoint URL** - Enter: `https://cortex.dgrzone.com/channels/google-chat/{username}` 7. Under **Visibility:** - Add the specific Google accounts that should be able to use this bot - For One Sky IT Workspace users: add individuals or the whole domain 8. Click **Save** > **Important:** The URL in step 6 must exactly match the `audience` value in `channels.json`. Google includes this URL as the JWT `aud` claim on every request, and Cortex rejects any request where they don't match. --- ## How It Works 1. User sends a message in Google Chat → Google POSTs a signed JSON payload to `/channels/google-chat/{username}` 2. Cortex reads the user's `channels.json`, verifies the JWT `systemIdToken` from `authorizationEventObject` 3. Sets the persona context, builds the system prompt, calls the LLM 4. Returns the response wrapped in `hostAppDataAction → chatDataAction → createMessageAction` The response must be returned synchronously (Google Chat does not support async/background replies like NC Talk does). The 25s timeout is a hard constraint. --- ## JWT Verification Google Chat Workspace Add-ons send a `systemIdToken` in the request body at: `body["authorizationEventObject"]["systemIdToken"]` Claims verified by Cortex: - `iss` = `https://accounts.google.com` - `aud` = the value of `audience` in `channels.json` If `audience` is empty, verification is skipped (useful for local testing, never in production). --- ## Nginx The `/channels/` prefix is already public in `auth_middleware.py` — no Nginx changes needed if you're already proxying all traffic to Cortex. Verify the path isn't blocked by basic auth or IP restrictions. --- ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | 404 on the webhook | `channels.json` missing or no `google_chat` key | Create/check `home/{username}/channels.json` | | 401 Invalid token | `audience` in `channels.json` doesn't match the endpoint URL | Make them identical — copy the URL exactly | | 401 Missing token | No `systemIdToken` in request | Bot may not be a Workspace Add-on; check connection settings type | | Timeout / no response | LLM too slow | `backend: "claude"` recommended; reduce context tier if needed | | Bot not receiving messages | Visibility not configured | Add the user's Google account under Visibility in Cloud Console |