Back to skills
SkillHub ClubShip Full StackFull Stack

google-chat

Send messages to Google Chat spaces and users via webhooks or OAuth. Use when you need to send notifications, alerts, or messages to Google Chat channels (spaces) or direct messages to specific users. Supports both incoming webhooks (for predefined channels) and OAuth 2.0 (for dynamic messaging to any space or user).

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
3,070
Hot score
99
Updated
March 20, 2026
Overall rating
C4.0
Composite score
4.0
Best-practice grade
B77.6

Install command

npx @skill-hub/cli install openclaw-skills-google-chat

Repository

openclaw/skills

Skill path: skills/darconada/google-chat

Send messages to Google Chat spaces and users via webhooks or OAuth. Use when you need to send notifications, alerts, or messages to Google Chat channels (spaces) or direct messages to specific users. Supports both incoming webhooks (for predefined channels) and OAuth 2.0 (for dynamic messaging to any space or user).

Open repository

Best for

Primary workflow: Ship Full Stack.

Technical facets: Full Stack.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: openclaw.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install google-chat into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/openclaw/skills before adding google-chat to shared team environments
  • Use google-chat for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: google-chat
description: Send messages to Google Chat spaces and users via webhooks or OAuth. Use when you need to send notifications, alerts, or messages to Google Chat channels (spaces) or direct messages to specific users. Supports both incoming webhooks (for predefined channels) and OAuth 2.0 (for dynamic messaging to any space or user).
---

# Google Chat Messaging

Send messages to Google Chat using two methods:

1. **Webhooks** - Fast, pre-configured channels (messages appear as a bot)
2. **OAuth** - Dynamic messaging to any space or user (requires authentication)

## Quick Start

### Method 1: Webhooks (Recommended for Known Channels)

Send to a pre-configured channel:

```bash
python3 scripts/send_webhook.py "$WEBHOOK_URL" "Your message here"
```

Example with threading:
```bash
python3 scripts/send_webhook.py "$WEBHOOK_URL" "Reply message" --thread_key "unique-thread-id"
```

**Configuration:** Store webhooks in `google-chat-config.json`:

```json
{
  "webhooks": {
    "acs_engineering_network": "https://chat.googleapis.com/v1/spaces/...",
    "general": "https://chat.googleapis.com/v1/spaces/..."
  }
}
```

Read config and send:
```bash
WEBHOOK_URL=$(jq -r '.webhooks.acs_engineering_network' google-chat-config.json)
python3 scripts/send_webhook.py "$WEBHOOK_URL" "Deploy completed āœ…"
```

### Method 2: OAuth (For Dynamic Messaging)

**First-time setup:**

1. Save OAuth credentials to a file (e.g., `google-chat-oauth-credentials.json`)
2. Run initial authentication (opens browser, saves token):

```bash
python3 scripts/send_oauth.py \
  --credentials google-chat-oauth-credentials.json \
  --token google-chat-token.json \
  --space "General" \
  "Test message"
```

**Send to a space by name:**
```bash
python3 scripts/send_oauth.py \
  --credentials google-chat-oauth-credentials.json \
  --token google-chat-token.json \
  --space "Engineering Network" \
  "Deploy completed"
```

**Note:** OAuth messages automatically include `šŸ¤–` emoji prefix. Use `--no-emoji` to disable this:
```bash
python3 scripts/send_oauth.py \
  --credentials google-chat-oauth-credentials.json \
  --token google-chat-token.json \
  --space "Engineering Network" \
  "Message without emoji" \
  --no-emoji
```

**List available spaces:**
```bash
python3 scripts/send_oauth.py \
  --credentials google-chat-oauth-credentials.json \
  --token google-chat-token.json \
  --list-spaces
```

**Send to a DM (requires existing space ID):**
```bash
# Note: Google Chat API doesn't support creating new DMs by email
# You need the space ID of an existing DM conversation
python3 scripts/send_oauth.py \
  --credentials google-chat-oauth-credentials.json \
  --token google-chat-token.json \
  --space-id "spaces/xxxxx" \
  "The report is ready"
```

**Send to space by ID (faster):**
```bash
python3 scripts/send_oauth.py \
  --credentials google-chat-oauth-credentials.json \
  --token google-chat-token.json \
  --space-id "spaces/AAAALtlqgVA" \
  "Direct message to space"
```

## Dependencies

Install required Python packages:

```bash
pip install google-auth-oauthlib google-auth-httplib2 google-api-python-client
```

**Required OAuth Scopes:**
- `https://www.googleapis.com/auth/chat.messages` - Send messages
- `https://www.googleapis.com/auth/chat.spaces` - Access space information
- `https://www.googleapis.com/auth/chat.memberships.readonly` - List space members (for DM identification)

## OAuth Setup Guide

If OAuth credentials don't exist yet:

1. Go to [Google Cloud Console](https://console.cloud.google.com)
2. Select your project or create one
3. Enable **Google Chat API**
4. Go to **APIs & Services → Credentials**
5. Create **OAuth 2.0 Client ID** (Desktop app type)
6. Download JSON and save as `google-chat-oauth-credentials.json`

The credentials JSON should look like:
```json
{
  "installed": {
    "client_id": "...apps.googleusercontent.com",
    "client_secret": "GOCSPX-...",
    "redirect_uris": ["http://localhost"],
    ...
  }
}
```

## Webhook Setup Guide

To create a webhook for a Google Chat space:

1. Open Google Chat in browser
2. Go to the space
3. Click space name → **Apps & integrations**
4. Click **Manage webhooks** → **Add webhook**
5. Give it a name (e.g., "Agustin Networks")
6. Copy the webhook URL
7. Add to `google-chat-config.json`

## Choosing the Right Method

**Use Webhooks when:**
- Sending to the same channels repeatedly
- Messages should appear as a bot/service
- Speed is important (no OAuth handshake)
- Configuration is static

**Use OAuth when:**
- Sending to different spaces dynamically
- Messages should appear from your configured Google Chat App
- Space names are determined at runtime
- Need to list and discover available spaces

**OAuth Limitations:**
- Cannot create new DMs by email address (Google Chat API restriction)
- To send DMs, you need the space ID of an existing conversation
- Use `--list-spaces` to find available DM space IDs

## Message Formatting

Both methods support simple text. For advanced formatting (cards, buttons), construct JSON payloads:

**Webhook with card:**
```python
import json
import urllib.request

payload = {
    "cardsV2": [{
        "cardId": "unique-card-id",
        "card": {
            "header": {"title": "Deploy Status"},
            "sections": [{
                "widgets": [{
                    "textParagraph": {"text": "Production deploy completed successfully"}
                }]
            }]
        }
    }]
}

data = json.dumps(payload).encode("utf-8")
req = urllib.request.Request(webhook_url, data=data, headers={"Content-Type": "application/json"})
urllib.request.urlopen(req)
```

## Troubleshooting

**Webhook errors:**
- Verify webhook URL is correct and active
- Check space still exists and webhook wasn't deleted
- Ensure message isn't empty

**OAuth errors:**
- Run authentication flow again if token expired
- Verify Google Chat API is enabled in Cloud Console
- Check user has access to the target space
- For DMs, ensure user email is correct and in same workspace

**Permission errors:**
- Webhooks: Must be member of the space
- OAuth: Must have access to target space or user
- Corporate Workspace: Some features may be restricted by admin policies

## Examples

**Deploy notification to engineering channel:**
```bash
WEBHOOK=$(jq -r '.webhooks.acs_engineering_network' google-chat-config.json)
python3 scripts/send_webhook.py "$WEBHOOK" "šŸš€ Production deploy v2.1.0 completed"
```

**Alert specific user about task:**
```bash
python3 scripts/send_oauth.py \
  --credentials google-chat-oauth-credentials.json \
  --token google-chat-token.json \
  --dm [email protected] \
  "Your report is ready for review: https://docs.company.com/report"
```

**Thread multiple messages together (webhook):**
```bash
WEBHOOK=$(jq -r '.webhooks.general' google-chat-config.json)
THREAD_KEY="deploy-$(date +%s)"

python3 scripts/send_webhook.py "$WEBHOOK" "Starting deploy..." --thread_key "$THREAD_KEY"
# ... deployment happens ...
python3 scripts/send_webhook.py "$WEBHOOK" "Deploy completed āœ…" --thread_key "$THREAD_KEY"
```


---

## Referenced Files

> The following files are referenced in this skill and included for context.

### scripts/send_webhook.py

```python
#!/usr/bin/env python3
"""
Send a message to Google Chat via Incoming Webhook.
Usage: python3 send_webhook.py <webhook_url> <message> [--thread_key <key>]
"""

import sys
import json
import urllib.request
import urllib.error
from typing import Optional


def send_webhook_message(webhook_url: str, message: str, thread_key: Optional[str] = None) -> dict:
    """Send a message via Google Chat webhook."""
    payload = {"text": message}
    
    # Add thread key if provided (for threading replies)
    url = webhook_url
    if thread_key:
        url = f"{webhook_url}&threadKey={thread_key}"
    
    headers = {"Content-Type": "application/json; charset=UTF-8"}
    data = json.dumps(payload).encode("utf-8")
    
    req = urllib.request.Request(url, data=data, headers=headers)
    
    try:
        with urllib.request.urlopen(req) as response:
            result = json.loads(response.read().decode("utf-8"))
            return {"success": True, "response": result}
    except urllib.error.HTTPError as e:
        error_body = e.read().decode("utf-8")
        return {"success": False, "error": f"HTTP {e.code}: {error_body}"}
    except Exception as e:
        return {"success": False, "error": str(e)}


def main():
    if len(sys.argv) < 3:
        print("Usage: python3 send_webhook.py <webhook_url> <message> [--thread_key <key>]")
        sys.exit(1)
    
    webhook_url = sys.argv[1]
    message = sys.argv[2]
    thread_key = None
    
    # Parse optional thread key
    if len(sys.argv) >= 5 and sys.argv[3] == "--thread_key":
        thread_key = sys.argv[4]
    
    result = send_webhook_message(webhook_url, message, thread_key)
    
    if result["success"]:
        print(json.dumps(result["response"], indent=2))
    else:
        print(f"Error: {result['error']}", file=sys.stderr)
        sys.exit(1)


if __name__ == "__main__":
    main()

```

### scripts/send_oauth.py

```python
#!/usr/bin/env python3
"""
Send a message to Google Chat via OAuth 2.0.
Usage: 
  # Send to a space by name
  python3 send_oauth.py --credentials creds.json --token token.json --space "Space Name" "Message"
  
  # Send DM to user email
  python3 send_oauth.py --credentials creds.json --token token.json --dm [email protected] "Message"
  
  # Send to space by ID
  python3 send_oauth.py --credentials creds.json --token token.json --space-id "spaces/AAAA..." "Message"
"""

import sys
import json
import argparse
from typing import Optional
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
import os


SCOPES = [
    'https://www.googleapis.com/auth/chat.messages',
    'https://www.googleapis.com/auth/chat.spaces',
    'https://www.googleapis.com/auth/chat.memberships.readonly'
]


def get_credentials(credentials_path: str, token_path: str) -> Credentials:
    """Get or refresh OAuth credentials."""
    creds = None
    
    # Load existing token if available
    if os.path.exists(token_path):
        creds = Credentials.from_authorized_user_file(token_path, SCOPES)
    
    # Refresh or create new credentials
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(credentials_path, SCOPES)
            print("\nšŸ” Authentication required!", file=sys.stderr)
            print("Opening browser for authentication...\n", file=sys.stderr)
            creds = flow.run_local_server(port=0)
        
        # Save credentials for next run
        with open(token_path, 'w') as token:
            token.write(creds.to_json())
    
    return creds


def find_space_by_name(service, space_name: str) -> Optional[str]:
    """Find a space ID by its display name."""
    try:
        result = service.spaces().list(pageSize=100).execute()
        spaces = result.get('spaces', [])
        
        for space in spaces:
            if space.get('displayName', '').lower() == space_name.lower():
                return space['name']
        
        return None
    except HttpError as e:
        print(f"Error listing spaces: {e}", file=sys.stderr)
        return None


def create_dm_space(service, user_email: str) -> Optional[str]:
    """Create or get a DM space with a user."""
    try:
        # List existing spaces to find DM
        result = service.spaces().list(pageSize=100).execute()
        spaces = result.get('spaces', [])
        
        # Look for existing DM with this user
        for space in spaces:
            if space.get('type') == 'DIRECT_MESSAGE' or space.get('spaceType') == 'DIRECT_MESSAGE':
                # Check if this DM includes the target user
                # For DMs, we can try to send and see if it works
                # This is a limitation - we can't easily find existing DMs by email
                pass
        
        # For now, we need the space ID directly for DMs
        # OAuth API doesn't easily support creating DMs by email
        print(f"Error: Cannot create DM to {user_email} directly.", file=sys.stderr)
        print(f"To send DMs via OAuth, you need the space ID.", file=sys.stderr)
        print(f"List available spaces with: --list-spaces", file=sys.stderr)
        return None
    except HttpError as e:
        print(f"Error: {e}", file=sys.stderr)
        return None


def send_message(service, space_id: str, message: str, add_emoji: bool = True) -> dict:
    """Send a message to a space."""
    try:
        # Add robot emoji prefix
        if add_emoji:
            message = f"šŸ¤– {message}"
        
        body = {'text': message}
        result = service.spaces().messages().create(
            parent=space_id,
            body=body
        ).execute()
        return {"success": True, "response": result}
    except HttpError as e:
        return {"success": False, "error": str(e)}


def list_spaces(service):
    """List all available spaces."""
    try:
        result = service.spaces().list(pageSize=100).execute()
        spaces = result.get('spaces', [])
        
        print("\n=== Available Spaces ===\n")
        for space in spaces:
            space_type = space.get('spaceType', space.get('type', 'UNKNOWN'))
            space_id = space['name']
            
            # For DMs, try to get member info
            if space_type == 'DIRECT_MESSAGE':
                try:
                    members_result = service.spaces().members().list(parent=space_id).execute()
                    members = members_result.get('memberships', [])
                    member_names = []
                    for member in members:
                        member_info = member.get('member', {})
                        display_name = member_info.get('displayName', 'Unknown')
                        member_names.append(display_name)
                    
                    name = f"DM: {', '.join(member_names)}"
                except:
                    name = space.get('displayName', 'DM (unknown participants)')
            else:
                name = space.get('displayName', space.get('name', 'Unnamed'))
            
            print(f"• {name}")
            print(f"  Type: {space_type}")
            print(f"  ID: {space_id}\n")
        
        return True
    except HttpError as e:
        print(f"Error listing spaces: {e}", file=sys.stderr)
        return False


def main():
    parser = argparse.ArgumentParser(description='Send Google Chat messages via OAuth')
    parser.add_argument('--credentials', required=True, help='Path to OAuth credentials JSON')
    parser.add_argument('--token', required=True, help='Path to token file (will be created if missing)')
    
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument('--space', help='Space display name')
    group.add_argument('--space-id', help='Space ID (spaces/...)')
    group.add_argument('--dm', help='User email for direct message')
    group.add_argument('--list-spaces', action='store_true', help='List all available spaces')
    
    parser.add_argument('message', nargs='?', help='Message to send')
    parser.add_argument('--no-emoji', action='store_true', help='Skip robot emoji prefix')
    
    args = parser.parse_args()
    
    # Get credentials
    creds = get_credentials(args.credentials, args.token)
    service = build('chat', 'v1', credentials=creds)
    
    # Handle list-spaces command
    if args.list_spaces:
        if list_spaces(service):
            sys.exit(0)
        else:
            sys.exit(1)
    
    # Validate message is provided for send operations
    if not args.message:
        print("Error: message is required when sending", file=sys.stderr)
        sys.exit(1)
    
    # Determine space ID
    space_id = None
    if args.space_id:
        space_id = args.space_id
    elif args.space:
        space_id = find_space_by_name(service, args.space)
        if not space_id:
            print(f"Error: Space '{args.space}' not found", file=sys.stderr)
            sys.exit(1)
    elif args.dm:
        space_id = create_dm_space(service, args.dm)
        if not space_id:
            print(f"Error: Could not create DM with {args.dm}", file=sys.stderr)
            sys.exit(1)
    
    # Send message
    result = send_message(service, space_id, args.message, add_emoji=not args.no_emoji)
    
    if result["success"]:
        print(json.dumps(result["response"], indent=2))
    else:
        print(f"Error: {result['error']}", file=sys.stderr)
        sys.exit(1)


if __name__ == "__main__":
    main()

```



---

## Skill Companion Files

> Additional files collected from the skill directory layout.

### README.md

```markdown
# Google Chat Skill

Send messages to Google Chat spaces and users via webhooks or OAuth 2.0.

## Features

āœ… **Webhook support** - Send to predefined channels (messages appear as configured bot)  
āœ… **OAuth support** - Send to any space dynamically (messages appear from your Google Chat App)  
āœ… **Space discovery** - List all available spaces and DMs  
āœ… **Automatic emoji prefix** - OAuth messages include šŸ¤– emoji (configurable)  
āœ… **Message threading** - Support for threaded conversations

## Quick Start

### Webhook (fastest)
```bash
python3 scripts/send_webhook.py "$WEBHOOK_URL" "Your message"
```

### OAuth (flexible)
```bash
# First time: authenticate
python3 scripts/send_oauth.py \
  --credentials oauth-creds.json \
  --token token.json \
  --space "Channel Name" \
  "Your message"

# List spaces
python3 scripts/send_oauth.py \
  --credentials oauth-creds.json \
  --token token.json \
  --list-spaces
```

## Setup Requirements

**For webhooks:**
- Create incoming webhook in Google Chat space settings

**For OAuth:**
1. Google Cloud Console → Create OAuth 2.0 credentials (Desktop app)
2. Enable Google Chat API
3. Download credentials JSON
4. Run authentication flow (opens browser)

## Configuration Example

See `references/config-example.json` for a config template with multiple webhooks.

## Limitations

- **OAuth cannot create new DMs by email** - This is a Google Chat API limitation
- To send DMs via OAuth, you need the space ID of an existing conversation
- Use `--list-spaces` to discover available DM space IDs

## Full Documentation

See `SKILL.md` for complete usage, examples, and troubleshooting.

---

**Created:** 2026-01-25  
**Tested with:** Google Workspace

```

### _meta.json

```json
{
  "owner": "darconada",
  "slug": "google-chat",
  "displayName": "Google Chat",
  "latest": {
    "version": "1.0.0",
    "publishedAt": 1769302886235,
    "commit": "https://github.com/clawdbot/skills/commit/0c6966a2879436913162379c00e81ae8918ad083"
  },
  "history": []
}

```

### references/config-example.json

```json
{
  "webhooks": {
    "my-channel": "https://chat.googleapis.com/v1/spaces/SPACE_ID/messages?key=YOUR_KEY&token=YOUR_TOKEN",
    "engineering": "https://chat.googleapis.com/v1/spaces/...",
    "alerts": "https://chat.googleapis.com/v1/spaces/..."
  },
  "oauth": {
    "credentials_path": "./google-chat-oauth-credentials.json",
    "token_path": "./google-chat-token.json"
  }
}

```

google-chat | SkillHub