Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Send Google Chat messages via incoming webhooks — text, rich cards (cardsV2), threaded replies. TypeScript types, card builder utility, widget reference inline. Use whenever the user wants to post to Google Chat from a script, build a chatbot reply, send a notification card, build a Google Chat webhook integration, or troubleshoot card / threading issues.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 86% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 86% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 246% | 0% |
| case-16 | ✗→✓ | ▲ Improved | 321% | 0% |
| case-18 | ✗→✓ | ▲ Improved | 177% | 0% |
Send messages to Google Chat spaces via incoming webhooks. Produces text messages, rich cards (cardsV2), and threaded replies.
In Google Chat:
Store the URL as an environment variable or in your secrets manager — never hardcode.
| Need | Type | Complexity | |------|------|------------| | Simple notification | Text message | Low | | Structured info (status, digest) | Card message (cardsV2) | Medium | | Ongoing updates | Threaded replies | Medium | | Action buttons (open URL) | Card with buttonList | Medium |
Use assets/webhook-sender.ts for the sender utility. Use assets/card-builder.ts for structured card construction.
Google Chat does NOT use standard Markdown.
| Format | Syntax | Example | |--------|--------|---------| | Bold | *text* | *important* | | Italic | _text_ | _emphasis_ | | Strikethrough | ~text~ | ~removed~ | | Monospace | text | code | | Code block | text | Multi-line code | | Link | <url\|text> | <https://example.com\|Click here> | | Mention user | <users/USER_ID> | <users/123456> | | Mention all | <users/all> | <users/all> |
Not supported: **double asterisks**, headings (###), blockquotes, tables, images inline.
typescriptawait sendText(webhookUrl, '*Build Complete*\n\nBranch: `main`\nStatus: Passed\n<https://ci.example.com/123|View Build>');
Cards use the cardsV2 format (recommended over legacy cards).
typescriptconst message = { cardsV2: [{ cardId: 'unique-id', card: { header: { title: 'Card Title', subtitle: 'Optional subtitle', imageUrl: 'https://example.com/icon.png', imageType: 'CIRCLE' // or 'SQUARE' }, sections: [{ header: 'Section Title', // optional widgets: [ // widgets go here ] }] } }] };
All widget types available in cardsV2 sections.
Formatted text block. Supports Google Chat formatting (*bold*, _italic_, <url|text>).
typescript{ textParagraph: { text: '*Status*: All systems operational\n_Last checked_: 5 minutes ago' } }
Labelled value with optional icons. Most versatile widget for key-value data.
Basic:
typescript{ decoratedText: { topLabel: 'Environment', text: 'Production', bottomLabel: 'Last deployed 2h ago' } }
With start icon:
typescript{ decoratedText: { topLabel: 'Status', text: 'Healthy', startIcon: { knownIcon: 'STAR' } } }
With custom icon URL:
typescript{ decoratedText: { topLabel: 'GitHub', text: 'PR #142 merged', startIcon: { iconUrl: 'https://github.githubassets.com/favicons/favicon.svg', altText: 'GitHub' } } }
With button:
typescript{ decoratedText: { topLabel: 'Alert', text: 'CPU at 95%', button: { text: 'View', onClick: { openLink: { url: 'https://monitoring.example.com' } } } } }
Clickable (whole widget):
typescript{ decoratedText: { text: 'View full report', wrapText: true, onClick: { openLink: { url: 'https://reports.example.com' } } } }
With wrap text:
typescript{ decoratedText: { topLabel: 'Description', text: 'This is a longer description that should wrap to multiple lines instead of being truncated', wrapText: true } }
One or more action buttons. Buttons open URLs or trigger actions.
Single button:
typescript{ buttonList: { buttons: [{ text: 'Open Dashboard', onClick: { openLink: { url: 'https://dashboard.example.com' } } }] } }
Multiple buttons:
typescript{ buttonList: { buttons: [ { text: 'Approve', onClick: { openLink: { url: 'https://app.example.com/approve/123' } }, color: { red: 0, green: 0.5, blue: 0, alpha: 1 } }, { text: 'Reject', onClick: { openLink: { url: 'https://app.example.com/reject/123' } } } ] } }
Button with icon:
typescript{ buttonList: { buttons: [{ text: 'View on GitHub', icon: { knownIcon: 'BOOKMARK' }, onClick: { openLink: { url: 'https://github.com/org/repo/pull/42' } } }] } }
Standalone image widget.
typescript{ image: { imageUrl: 'https://example.com/chart.png', altText: 'Monthly usage chart' } }
Horizontal line separator between widgets.
typescript{ divider: {} }
Sections can be collapsed with only the first N widgets visible:
typescript{ header: 'Details', collapsible: true, uncollapsibleWidgetsCount: 2, // Show first 2, collapse rest widgets: [ { decoratedText: { topLabel: 'Status', text: 'Active' } }, { decoratedText: { topLabel: 'Region', text: 'AU' } }, // These start collapsed { decoratedText: { topLabel: 'Instance', text: 'prod-01' } }, { decoratedText: { topLabel: 'Memory', text: '2.1 GB' } }, { decoratedText: { topLabel: 'CPU', text: '45%' } } ] }
Icons available via knownIcon in decoratedText and button widgets.
typescript{ startIcon: { knownIcon: 'STAR' } } // or { icon: { knownIcon: 'EMAIL' } }
| Icon Name | Use For | |-----------|---------| | AIRPLANE | Travel, flights | | BOOKMARK | Save, reference, links | | BUS | Transport, transit | | CAR | Driving, transport | | CLOCK | Time, duration, schedule | | CONFIRMATION_NUMBER_ICON | Tickets, bookings | | DESCRIPTION | Documents, files | | DOLLAR | Money, pricing, cost | | EMAIL | Email, messages | | INVITE | Invitations | | MAP_PIN | Location, address | | MEMBERSHIP | Members, users | | MULTIPLE_PEOPLE | Teams, groups | | OFFER | Deals, promotions | | PERSON | Individual user | | PHONE | Phone number, calls | | SHOPPING_CART | Commerce, purchases | | STAR | Rating, favourite, important | | STORE | Shop, retail | | TICKET | Tickets, events | | VIDEO_CAMERA | Video, meetings |
For icons not in the list, use iconUrl with any publicly accessible image (square, ideally 24x24 or 48x48 pixels).
Thread messages together using threadKey:
typescript// First message — creates thread const response = await sendCard(webhookUrl, card, { threadKey: 'deploy-2026-02-16' }); // Reply to thread — append &messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD const threadUrl = `${webhookUrl}&messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD`; await sendCard(threadUrl, replyCard, { threadKey: 'deploy-2026-02-16' });
The threadKey is a client-assigned string. Use consistent keys for related messages (e.g., deploy-{date}, alert-{id}).
typescriptimport { buildCard, sendCard } from './assets/card-builder'; import { sendWebhook } from './assets/webhook-sender'; const card = buildCard({ cardId: 'deploy-notification', title: 'Deployment Complete', subtitle: 'production - v2.1.0', imageUrl: 'https://example.com/your-icon.png', sections: [{ widgets: [ { decoratedText: { topLabel: 'Environment', text: 'Production' } }, { decoratedText: { topLabel: 'Version', text: 'v2.1.0' } }, { decoratedText: { topLabel: 'Status', text: '*Healthy*', startIcon: { knownIcon: 'STAR' } } }, { buttonList: { buttons: [{ text: 'View Deployment', onClick: { openLink: { url: 'https://dash.example.com' } } }] } } ] }] });
typescriptconst digest = buildCard({ cardId: 'weekly-digest', title: 'Weekly Summary', subtitle: `${count} updates this week`, sections: [ { header: 'Highlights', widgets: items.map(item => ({ decoratedText: { text: item.title, bottomLabel: item.date } })) }, { widgets: [{ buttonList: { buttons: [{ text: 'View All', onClick: { openLink: { url: dashboardUrl } } }] } }] } ] });
| Mistake | Fix | |---------|-----| | **bold** in text | Use *bold* (single asterisks) | | [text](url) links | Use <url\|text> format | | Missing cardsV2 wrapper | Wrap card in { cardsV2: [{ cardId, card }] } | | Thread replies not threading | Append &messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD to webhook URL | | Webhook returns 400 | Check JSON structure — common issue is missing text or cardsV2 at top level | | Card not showing | Ensure sections has at least one widget |
| File | Purpose | |------|---------| | assets/types.ts | TypeScript type definitions for cardsV2 | | assets/card-builder.ts | Utility to build card messages | | assets/webhook-sender.ts | POST to webhook with error handling |
Other measured skills in the registry, with their headline benchmark lift.