| name | google-chat-api |
| description | Build Google Chat bots and webhooks with Cards v2, interactive forms, and Cloudflare Workers integration. Includes webhook handlers, card builders, form validation, bearer token verification, and dialog patterns. Use when creating Chat bots, notification systems, workflow automation, or interactive forms, or troubleshooting bearer token errors, card schema validation, widget limits, or webhook verification failures. |
Google Chat API
Status: Production Ready Last Updated: 2025-11-29 (Research verified: Markdown support, Material Design updates) Dependencies: Cloudflare Workers (recommended), Web Crypto API for token verification Latest Versions: Google Chat API v1 (stable), Cards v2 (Cards v1 deprecated)
Quick Start (5 Minutes)
1. Create Webhook (Simplest Approach)
# No code needed - just configure in Google Chat
# 1. Go to Google Cloud Console
# 2. Create new project or select existing
# 3. Enable Google Chat API
# 4. Configure Chat app with webhook URL
Webhook URL: https://your-worker.workers.dev/webhook
Why this matters:
- Simplest way to send messages to Chat
- No authentication required for incoming webhooks
- Perfect for notifications from external systems
- Limited to sending messages (no interactive responses)
2. Create Interactive Bot (Cloudflare Worker)
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const event = await request.json()
// Respond with a card
return Response.json({
text: "Hello from bot!",
cardsV2: [{
cardId: "unique-card-1",
card: {
header: { title: "Welcome" },
sections: [{
widgets: [{
textParagraph: { text: "Click the button below" }
}, {
buttonList: {
buttons: [{
text: "Click me",
onClick: {
action: {
function: "handleClick",
parameters: [{ key: "data", value: "test" }]
}
}
}]
}
}]
}]
}
}]
})
}
}
CRITICAL:
- Must respond within timeout (typically 30 seconds)
- Always return valid JSON with
cardsV2array - Card schema must be exact - one wrong field breaks the whole card
3. Verify Bearer Tokens (Production Security)
async function verifyToken(token: string): Promise<boolean> {
// Verify token is signed by chat@system.gserviceaccount.com
// See templates/bearer-token-verify.ts for full implementation
return true
}
Why this matters:
- Prevents unauthorized access to your bot
- Required for HTTP endpoints (not webhooks)
- Uses Web Crypto API (Cloudflare Workers compatible)
The 3-Step Setup Process
Step 1: Choose Integration Type
Option A: Incoming Webhook (Notifications Only)
Best for:
- CI/CD notifications
- Alert systems
- One-way communication
- External service → Chat
Setup:
- Create Chat space
- Configure incoming webhook in Space settings
- POST JSON to webhook URL
No code required - just HTTP POST:
curl -X POST 'https://chat.googleapis.com/v1/spaces/.../messages?key=...' \
-H 'Content-Type: application/json' \
-d '{"text": "Hello from webhook!"}'
Option B: HTTP Endpoint Bot (Interactive)
Best for:
- Interactive forms
- Button-based workflows
- User input collection
- Chat → Your service → Chat
Setup:
- Create Google Cloud project
- Enable Chat API
- Configure Chat app with HTTP endpoint
- Deploy Cloudflare Worker
- Handle events and respond with cards
Requires code - see templates/interactive-bot.ts
Step 2: Design Cards (If Using Interactive Bot)
IMPORTANT: Use Cards v2 only. Cards v1 was deprecated in 2025. Cards v2 matches Material Design on web (faster rendering, better aesthetics).
Cards v2 structure:
{
"cardsV2": [{
"cardId": "unique-id",
"card": {
"header": {
"title": "Card Title",
"subtitle": "Optional subtitle",
"imageUrl": "https://..."
},
"sections": [{
"header": "Section 1",
"widgets": [
{ "textParagraph": { "text": "Some text" } },
{ "buttonList": { "buttons": [...] } }
]
}]
}
}]
}
Widget Types:
textParagraph- Text contentbuttonList- Buttons (text or icon)textInput- Text input fieldselectionInput- Dropdowns, checkboxes, switchesdateTimePicker- Date/time selectiondivider- Horizontal lineimage- ImagesdecoratedText- Text with icon/button
Text Formatting (NEW: Sept 2025 - GA):
Cards v2 supports both HTML and Markdown formatting:
// HTML formatting (traditional)
{
textParagraph: {
text: "This is <b>bold</b> and <i>italic</i> text with <font color='#ea9999'>color</font>"
}
}
// Markdown formatting (NEW - better for AI agents)
{
textParagraph: {
text: "This is **bold** and *italic* text\n\n- Bullet list\n- Second item\n\n```\ncode block\n```"
}
}
Supported Markdown (text messages and cards):
**bold**or*italic*`code`for inline code- list itemor1. orderedfor lists```code block```for multi-line code~strikethrough~
Supported HTML (cards only):
<b>bold</b>,<i>italic</i>,<u>underline</u><font color="#FF0000">colored</font><a href="url">link</a>
Why Markdown matters: LLMs naturally output Markdown. Before Sept 2025, you had to convert Markdown→HTML. Now you can pass Markdown directly to Chat.
CRITICAL:
- Max 100 widgets per card - silently truncated if exceeded
- Widget order matters - displayed top to bottom
- cardId must be unique - use timestamp or UUID
Step 3: Handle User Interactions
When user clicks button or submits form:
export default {
async fetch(request: Request): Promise<Response> {
const event = await request.json()
// Check event type
if (event.type === 'MESSAGE') {
// User sent message
return handleMessage(event)
}
if (event.type === 'CARD_CLICKED') {
// User clicked button
const action = event.action.actionMethodName
const params = event.action.parameters
if (action === 'submitForm') {
return handleFormSubmission(event)
}
}
return Response.json({ text: "Unknown event" })
}
}
Event Types:
ADDED_TO_SPACE- Bot added to spaceREMOVED_FROM_SPACE- Bot removedMESSAGE- User sent messageCARD_CLICKED- User clicked button/submitted form
Critical Rules
Always Do
✅ Return valid JSON with cardsV2 array structure
✅ Set unique cardId for each card
✅ Verify bearer tokens for HTTP endpoints (production)
✅ Handle all event types (MESSAGE, CARD_CLICKED, etc.)
✅ Keep widget count under 100 per card
✅ Validate form inputs server-side
Never Do
❌ Store secrets in code (use Cloudflare Workers secrets) ❌ Exceed 100 widgets per card (silently fails) ❌ Return malformed JSON (breaks entire message) ❌ Skip bearer token verification (security risk) ❌ Trust client-side validation only (validate server-side) ❌ Use synchronous blocking operations (timeout risk)
Known Issues Prevention
This skill prevents 5 documented issues:
Issue #1: Bearer Token Verification Fails (401)
Error: "Unauthorized" or "Invalid credentials" Source: Google Chat API Documentation Why It Happens: Token not verified or wrong verification method Prevention: Template includes Web Crypto API verification (Cloudflare Workers compatible)
Issue #2: Invalid Card JSON Schema (400)
Error: "Invalid JSON payload" or "Unknown field"
Source: Cards v2 API Reference
Why It Happens: Typo in field name, wrong nesting, or extra fields
Prevention: Use google-chat-cards library or templates with exact schema
Issue #3: Widget Limit Exceeded (Silent Failure)
Error: No error - widgets beyond 100 simply don't render Source: Google Chat API Limits Why It Happens: Adding too many widgets to single card Prevention: Skill documents 100 widget limit + pagination patterns
Issue #4: Form Validation Error Format Wrong
Error: Form doesn't show validation errors to user Source: Interactive Cards Documentation Why It Happens: Wrong error response format Prevention: Templates include correct error format:
{
"actionResponse": {
"type": "DIALOG",
"dialogAction": {
"actionStatus": {
"statusCode": "INVALID_ARGUMENT",
"userFacingMessage": "Email is required"
}
}
}
}
Issue #5: Webhook "Unable to Connect" Error
Error: Chat shows "Unable to connect to bot" Source: Webhook Setup Guide Why It Happens: URL not publicly accessible, timeout, or wrong response format Prevention: Skill includes timeout handling + response format validation
Configuration Files Reference
Cloudflare Worker (wrangler.jsonc)
{
"name": "google-chat-bot",
"main": "src/index.ts",
"compatibility_date": "2025-11-29",
"compatibility_flags": ["nodejs_compat"],
// Secrets (set with: wrangler secret put CHAT_BOT_TOKEN)
"vars": {
"ALLOWED_SPACES": "spaces/SPACE_ID_1,spaces/SPACE_ID_2"
}
}
Why these settings:
nodejs_compat- Required for Web Crypto API (token verification)- Secrets stored securely (not in code)
- Environment variables for configuration
Common Patterns
Pattern 1: Notification Bot (Webhook)
// External service sends notification to Chat
async function sendNotification(webhookUrl: string, message: string) {
await fetch(webhookUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: message,
cardsV2: [{
cardId: `notif-${Date.now()}`,
card: {
header: { title: "Alert" },
sections: [{
widgets: [{
textParagraph: { text: message }
}]
}]
}
}]
})
})
}
When to use: CI/CD alerts, monitoring notifications, event triggers
Pattern 2: Interactive Form
// Show form to collect data
function showForm() {
return {
cardsV2: [{
cardId: "form-card",
card: {
header: { title: "Enter Details" },
sections: [{
widgets: [
{
textInput: {
name: "email",
label: "Email",
type: "SINGLE_LINE",
hintText: "user@example.com"
}
},
{
selectionInput: {
name: "priority",
label: "Priority",
type: "DROPDOWN",
items: [
{ text: "Low", value: "low" },
{ text: "High", value: "high" }
]
}
},
{
buttonList: {
buttons: [{
text: "Submit",
onClick: {
action: {
function: "submitForm",
parameters: [{
key: "formId",
value: "contact-form"
}]
}
}
}]
}
}
]
}]
}
}]
}
}
When to use: Data collection, approval workflows, ticket creation
Pattern 3: Dialog (Modal)
// Open modal dialog
function openDialog() {
return {
actionResponse: {
type: "DIALOG",
dialogAction: {
dialog: {
body: {
sections: [{
header: "Confirm Action",
widgets: [{
textParagraph: { text: "Are you sure?" }
}, {
buttonList: {
buttons: [
{
text: "Confirm",
onClick: {
action: { function: "confirm" }
}
},
{
text: "Cancel",
onClick: {
action: { function: "cancel" }
}
}
]
}
}]
}]
}
}
}
}
}
}
When to use: Confirmations, multi-step workflows, focused data entry
Using Bundled Resources
Scripts (scripts/)
No executable scripts for this skill.
Templates (templates/)
Required for all projects:
templates/webhook-handler.ts- Basic webhook receivertemplates/wrangler.jsonc- Cloudflare Workers config
Optional based on needs:
templates/interactive-bot.ts- HTTP endpoint with event handlingtemplates/card-builder-examples.ts- Common card patternstemplates/form-validation.ts- Input validation with error responsestemplates/bearer-token-verify.ts- Token verification utility
When to load these: Claude should reference templates when user asks to:
- Set up Google Chat bot
- Create interactive cards
- Add form validation
- Verify bearer tokens
- Handle button clicks
References (references/)
references/google-chat-docs.md- Key documentation linksreferences/cards-v2-schema.md- Complete card structure referencereferences/common-errors.md- Error troubleshooting guide
When Claude should load these: Troubleshooting errors, designing cards, understanding API
Advanced Topics
Slash Commands
Register slash commands for quick actions:
// User types: /create-ticket Bug in login
if (event.message?.slashCommand?.commandName === 'create-ticket') {
const text = event.message.argumentText
return Response.json({
text: `Creating ticket: ${text}`,
cardsV2: [/* ticket confirmation card */]
})
}
Use cases: Quick actions, shortcuts, power user features
Thread Replies
Reply in existing thread:
return Response.json({
text: "Reply in thread",
thread: {
name: event.message.thread.name // Use existing thread
}
})
Use cases: Conversations, follow-ups, grouped discussions
Dependencies
Required:
- Cloudflare Workers account (free tier works)
- Google Cloud Project with Chat API enabled
- Public HTTPS endpoint (Workers provides this)
Optional:
google-chat-cards@1.0.3- Type-safe card builder (unofficial)- Web Crypto API (built into Cloudflare Workers)
Official Documentation
- Google Chat API: https://developers.google.com/workspace/chat
- Cards v2 Reference: https://developers.google.com/workspace/chat/api/reference/rest/v1/cards
- Webhooks Guide: https://developers.google.com/workspace/chat/quickstart/webhooks
- Interactive Cards: https://developers.google.com/workspace/chat/dialogs
- Cloudflare Workers: https://developers.cloudflare.com/workers
Package Versions (Verified 2025-11-29)
{
"dependencies": {
"google-chat-cards": "^1.0.3"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20250104.0",
"wrangler": "^3.95.0"
}
}
Note: No official Google Chat npm package - use fetch API directly.
Production Example
This skill is based on real-world implementations:
- Community Examples: translatebot (Worker + Chat + Translate API)
- Official Samples: Multiple working examples in Google's documentation
Token Savings: ~65-70% (8k → 2.5k tokens) Errors Prevented: 5/5 critical setup errors Validation: ✅ Webhook handlers, ✅ Card builders, ✅ Token verification, ✅ Form validation
Troubleshooting
Problem: "Unauthorized" (401) error
Solution: Implement bearer token verification (see templates/bearer-token-verify.ts)
Problem: Cards don't render / "Invalid JSON payload"
Solution: Validate card JSON against Cards v2 schema, ensure exact field names
Problem: Widgets beyond first 100 don't show
Solution: Split into multiple cards or use pagination
Problem: Form validation errors not showing to user
Solution: Return correct error format with actionResponse.dialogAction.actionStatus
Problem: "Unable to connect to bot"
Solution: Ensure URL is publicly accessible, responds within timeout, returns valid JSON
Complete Setup Checklist
Use this checklist to verify your setup:
- Google Cloud project created
- Chat API enabled in project
- Chat app configured with webhook/HTTP endpoint URL
- Cloudflare Worker deployed and accessible
- Bearer token verification implemented (if using HTTP endpoint)
- Card JSON validated against schema
- Widget count under 100 per card
- Form validation returns correct error format
- Tested in Chat space successfully
- Error handling for all event types
Questions? Issues?
- Check
references/common-errors.mdfor troubleshooting - Verify card JSON structure matches Cards v2 schema
- Check official docs: https://developers.google.com/workspace/chat
- Ensure bearer token verification is implemented for HTTP endpoints