Skip to main content

Google Services API

The Google Services API provides access to Google Calendar, Gmail, and Google Chat through OAuth 2.0 authentication. Each service requires user authorization and automatically handles token refresh.

Overview

The Google integration enables:

  • Google Calendar: Manage calendars and events
  • Gmail: Access emails, threads, and labels
  • Google Chat: Send and receive messages in spaces
  • OAuth 2.0: Secure per-user authorization with automatic token refresh
  • Unified Authentication: Single OAuth flow for all Google services
  • Token Management: Automatic access token refresh on expiration

Base URL

http://localhost:3007/google

Authentication Flow

1. OAuth Authorization

Before using Google services, users must complete OAuth authorization:

GET /oauth/google/authorize

Query parameters:

  • tenant_id (required): Tenant identifier
  • user_email (required): User's email address

Example:

curl "http://localhost:4002/oauth/google/authorize?tenant_id=acme&user_email=user@example.com"

This redirects to Google's authorization page. After approval, the user is redirected to the callback URL with authorization tokens.

2. Check Authorization Status

GET /oauth/google/status

Headers:

  • X-JWT-Token: JWT token with user_email

Returns:

{
"status": "authorized",
"user_email": "user@example.com",
"scopes": ["calendar", "gmail", "chat"],
"expires_at": "2026-01-13T10:00:00Z"
}

Status values:

  • authorized: User has valid tokens
  • needs_oauth: User needs to authorize
  • no_access: User has no access configured

3. Revoke Authorization

DELETE /oauth/google/revoke

Headers:

  • X-JWT-Token: JWT token with user_email

Google Calendar API

List All Events (All Calendars)

GET /google/calendar/events

Returns events from all user's calendars, sorted by start time.

Query parameters:

  • time_min (optional): Lower bound (RFC3339 timestamp, e.g., "2026-01-01T00:00:00Z")
  • time_max (optional): Upper bound (RFC3339 timestamp)
  • max_results_per_calendar (optional): Max events per calendar (1-500, default: 100)

Headers:

  • X-JWT-Token: JWT token with user_email

Example:

curl -H "X-JWT-Token: eyJ..." \
"http://localhost:3007/google/calendar/events?time_min=2026-01-12T00:00:00Z"

Response:

{
"success": true,
"data": [
{
"id": "event123",
"summary": "Team Standup",
"start": {
"dateTime": "2026-01-12T09:00:00-08:00",
"timeZone": "America/Los_Angeles"
},
"end": {
"dateTime": "2026-01-12T09:30:00-08:00",
"timeZone": "America/Los_Angeles"
},
"calendar_id": "primary",
"calendar_name": "My Calendar",
"location": "Conference Room A",
"attendees": [
{"email": "alice@example.com", "responseStatus": "accepted"}
]
}
],
"count": 1
}

List Calendars

GET /google/calendar/calendars

Returns all calendars accessible to the user.

Headers:

  • X-JWT-Token: JWT token with user_email

Response:

{
"success": true,
"data": [
{
"id": "primary",
"summary": "My Calendar",
"description": "Personal calendar",
"timeZone": "America/Los_Angeles",
"accessRole": "owner"
}
],
"count": 1
}

Get Calendar

GET /google/calendar/calendars/{calendar_id}

Get details of a specific calendar.

Path parameters:

  • calendar_id: Calendar ID (use "primary" for primary calendar)

Headers:

  • X-JWT-Token: JWT token with user_email

List Events (Single Calendar)

GET /google/calendar/calendars/{calendar_id}/events

List events in a specific calendar.

Path parameters:

  • calendar_id: Calendar ID (default: "primary")

Query parameters:

  • time_min (optional): Lower bound (RFC3339 timestamp)
  • time_max (optional): Upper bound (RFC3339 timestamp)
  • max_results (optional): Maximum events (1-2500, default: 100)
  • order_by (optional): "startTime" or "updated" (default: "startTime")

Headers:

  • X-JWT-Token: JWT token with user_email

Get Event

GET /google/calendar/calendars/{calendar_id}/events/{event_id}

Get details of a specific event.

Path parameters:

  • calendar_id: Calendar ID
  • event_id: Event ID

Headers:

  • X-JWT-Token: JWT token with user_email

Create Event

POST /google/calendar/calendars/{calendar_id}/events

Create a new calendar event.

Path parameters:

  • calendar_id: Calendar ID (use "primary" for primary calendar)

Headers:

  • X-JWT-Token: JWT token with user_email

Request body:

{
"summary": "Team Meeting",
"start_datetime": "2026-01-15T10:00:00",
"start_timezone": "America/Los_Angeles",
"end_datetime": "2026-01-15T11:00:00",
"end_timezone": "America/Los_Angeles",
"description": "Weekly team sync",
"location": "Conference Room A",
"attendee_emails": ["alice@example.com", "bob@example.com"]
}

Response:

{
"success": true,
"data": {
"id": "event456",
"summary": "Team Meeting",
"htmlLink": "https://calendar.google.com/event?eid=..."
}
}

Update Event

PUT /google/calendar/calendars/{calendar_id}/events/{event_id}

Update an existing event.

Path parameters:

  • calendar_id: Calendar ID
  • event_id: Event ID

Headers:

  • X-JWT-Token: JWT token with user_email

Request body (all fields optional):

{
"summary": "Updated Meeting Title",
"start_datetime": "2026-01-15T14:00:00",
"start_timezone": "America/Los_Angeles",
"end_datetime": "2026-01-15T15:00:00",
"end_timezone": "America/Los_Angeles",
"description": "Updated description",
"location": "New Location"
}

Delete Event

DELETE /google/calendar/calendars/{calendar_id}/events/{event_id}

Delete a calendar event.

Path parameters:

  • calendar_id: Calendar ID
  • event_id: Event ID

Headers:

  • X-JWT-Token: JWT token with user_email

Gmail API

List Threads

GET /google/gmail/threads

List email threads with metadata (sender, receiver, timestamps, snippet).

Query parameters:

  • max_results (optional): Maximum threads (1-500, default: 100)
  • query (optional): Gmail search query (e.g., "is:unread", "from:alice@example.com")
  • label_ids (optional): Comma-separated label IDs (e.g., "INBOX,UNREAD")

Headers:

  • X-JWT-Token: JWT token with user_email

Example:

curl -H "X-JWT-Token: eyJ..." \
"http://localhost:3007/google/gmail/threads?query=is:unread&max_results=50"

Response:

{
"success": true,
"data": [
{
"id": "thread123",
"snippet": "Meeting notes from yesterday...",
"historyId": "12345"
}
],
"count": 1
}

Get Thread

GET /google/gmail/threads/{thread_id}

Get all messages in a specific thread with complete metadata.

Path parameters:

  • thread_id: Thread ID

Query parameters:

  • format (optional): "full", "metadata", or "minimal" (default: "full")

Headers:

  • X-JWT-Token: JWT token with user_email

Response:

{
"success": true,
"data": {
"id": "thread123",
"messages": [
{
"id": "msg456",
"threadId": "thread123",
"labelIds": ["INBOX", "UNREAD"],
"snippet": "Meeting notes...",
"payload": {
"headers": [
{"name": "From", "value": "alice@example.com"},
{"name": "To", "value": "bob@example.com"},
{"name": "Subject", "value": "Meeting Notes"},
{"name": "Date", "value": "Mon, 12 Jan 2026 09:00:00 -0800"}
],
"body": {
"data": "SGVsbG8gV29ybGQ="
}
}
}
]
}
}

List Messages

GET /google/gmail/messages

List individual messages (returns only IDs and threadIds).

Query parameters:

  • max_results (optional): Maximum messages (1-500, default: 100)
  • query (optional): Gmail search query
  • label_ids (optional): Comma-separated label IDs

Headers:

  • X-JWT-Token: JWT token with user_email

Response:

{
"success": true,
"data": [
{
"id": "msg123",
"threadId": "thread456"
}
],
"count": 1
}

Get Message

GET /google/gmail/messages/{message_id}

Get a specific message by ID with full details.

Path parameters:

  • message_id: Message ID

Query parameters:

  • format (optional): "full", "metadata", "minimal", or "raw" (default: "full")

Headers:

  • X-JWT-Token: JWT token with user_email

Send Message

POST /google/gmail/messages/send

Send an email message.

Headers:

  • X-JWT-Token: JWT token with user_email

Request body:

{
"to": "alice@example.com",
"subject": "Hello",
"body": "This is the email body",
"cc": "bob@example.com",
"bcc": "charlie@example.com"
}

Response:

{
"success": true,
"data": {
"id": "msg789",
"threadId": "thread101",
"labelIds": ["SENT"]
}
}

Trash Message

POST /google/gmail/messages/{message_id}/trash

Move a message to trash.

Path parameters:

  • message_id: Message ID

Headers:

  • X-JWT-Token: JWT token with user_email

Modify Message Labels

POST /google/gmail/messages/{message_id}/modify

Add or remove labels from a message.

Path parameters:

  • message_id: Message ID

Headers:

  • X-JWT-Token: JWT token with user_email

Request body:

{
"add_label_ids": ["STARRED", "IMPORTANT"],
"remove_label_ids": ["UNREAD"]
}

List Labels

GET /google/gmail/labels

List all labels in the user's mailbox.

Headers:

  • X-JWT-Token: JWT token with user_email

Response:

{
"success": true,
"data": [
{
"id": "INBOX",
"name": "INBOX",
"type": "system"
},
{
"id": "Label_123",
"name": "Important Projects",
"type": "user"
}
],
"count": 2
}

Get Label

GET /google/gmail/labels/{label_id}

Get details of a specific label.

Path parameters:

  • label_id: Label ID

Headers:

  • X-JWT-Token: JWT token with user_email

Google Chat API

List Spaces

GET /google/chat/spaces

List all spaces (rooms/DMs) the user has access to.

Query parameters:

  • max_results (optional): Maximum spaces (1-1000, default: 100)
  • filter (optional): Filter expression (e.g., "spaceType = SPACE")

Headers:

  • X-JWT-Token: JWT token with user_email

Response:

{
"success": true,
"data": [
{
"name": "spaces/AAAAAbCdEfG",
"type": "SPACE",
"displayName": "Engineering Team",
"spaceThreadingState": "THREADED_MESSAGES"
}
],
"count": 1
}

Get Space

GET /google/chat/spaces/{space_id}

Get details of a specific space.

Path parameters:

  • space_id: Space ID (with or without "spaces/" prefix)

Headers:

  • X-JWT-Token: JWT token with user_email

List Messages

GET /google/chat/spaces/{space_id}/messages

List messages in a space.

Path parameters:

  • space_id: Space ID

Query parameters:

  • max_results (optional): Maximum messages (1-1000, default: 100)

Headers:

  • X-JWT-Token: JWT token with user_email

Response:

{
"success": true,
"data": [
{
"name": "spaces/AAAAAbCdEfG/messages/xyz123",
"text": "Hello team!",
"sender": {
"name": "users/123456789",
"displayName": "Alice",
"email": "alice@example.com"
},
"createTime": "2026-01-12T09:30:00Z"
}
],
"count": 1
}

Get Message

GET /google/chat/spaces/{space_id}/messages/{message_id}

Get a specific message.

Path parameters:

  • space_id: Space ID
  • message_id: Message ID

Headers:

  • X-JWT-Token: JWT token with user_email

Send Message

POST /google/chat/spaces/{space_id}/messages

Send a message to a space.

Path parameters:

  • space_id: Space ID

Headers:

  • X-JWT-Token: JWT token with user_email

Request body:

{
"text": "Hello team! This is a message from the API.",
"thread_key": "thread123"
}

Response:

{
"success": true,
"data": {
"name": "spaces/AAAAAbCdEfG/messages/xyz456",
"text": "Hello team! This is a message from the API.",
"createTime": "2026-01-12T10:00:00Z"
}
}

Update Message

PUT /google/chat/spaces/{space_id}/messages/{message_id}

Update an existing message.

Path parameters:

  • space_id: Space ID
  • message_id: Message ID

Headers:

  • X-JWT-Token: JWT token with user_email

Request body:

{
"text": "Updated message text"
}

Delete Message

DELETE /google/chat/spaces/{space_id}/messages/{message_id}

Delete a message.

Path parameters:

  • space_id: Space ID
  • message_id: Message ID

Headers:

  • X-JWT-Token: JWT token with user_email

List Members

GET /google/chat/spaces/{space_id}/members

List members of a space.

Path parameters:

  • space_id: Space ID

Query parameters:

  • max_results (optional): Maximum members (1-1000, default: 100)

Headers:

  • X-JWT-Token: JWT token with user_email

Response:

{
"success": true,
"data": [
{
"name": "spaces/AAAAAbCdEfG/members/123",
"member": {
"name": "users/123456789",
"displayName": "Alice",
"email": "alice@example.com"
},
"role": "ROLE_MEMBER",
"createTime": "2026-01-01T00:00:00Z"
}
],
"count": 1
}

Get Member

GET /google/chat/spaces/{space_id}/members/{member_id}

Get details of a specific member.

Path parameters:

  • space_id: Space ID
  • member_id: Member ID

Headers:

  • X-JWT-Token: JWT token with user_email

Error Handling

All Google API endpoints return standard error responses:

OAuth Required

When a user hasn't authorized or their tokens have expired:

{
"detail": {
"action": "oauth_required",
"message": "User needs to authorize Google access",
"auth_url": "http://localhost:4002/oauth/google/authorize?tenant_id=acme&user_email=user@example.com"
}
}

Status code: 401 Unauthorized

Email Mismatch

When OAuth email doesn't match JWT token email:

{
"detail": "Email mismatch: You authenticated with alice@gmail.com but the authorization was requested for bob@example.com. Please use the correct Google account."
}

Status code: 403 Forbidden

API Errors

Google API errors are passed through with details:

{
"detail": "Google Calendar API error: {\"error\": {\"code\": 404, \"message\": \"Not Found\"}}"
}

Status codes: Vary based on Google API response


Rate Limits

Google APIs have rate limits that vary by service:

  • Calendar API: 1,000,000 queries per day
  • Gmail API: 250 quota units per second per user
  • Chat API: 60 requests per minute per user

The API automatically handles rate limits with exponential backoff and retry logic.


Token Management

Automatic Refresh

Access tokens expire after 1 hour. The API automatically refreshes them using refresh tokens when:

  • A 401 error is received from Google
  • The stored token is expired

Token Storage

Tokens are stored in Redis with the following structure:

Key: auth_tokens:{tenant_id}:{user_email}:google
Value: {
"access_token": "ya29.a0...",
"refresh_token": "1//0e...",
"expires_at": "2026-01-12T10:00:00Z",
"scopes": ["calendar", "gmail", "chat"]
}

Security

  • OAuth flow uses JWT-encoded state tokens for CSRF protection
  • Refresh tokens are stored securely in Redis
  • Email verification ensures the OAuth user matches the JWT token user
  • All endpoints require valid JWT tokens with user_email

Example Workflow

1. Generate JWT Token

curl -X POST http://localhost:3007/admin/token \
-H "Content-Type: application/json" \
-d '{
"tenant_id": "acme",
"user_email": "user@example.com",
"user_display_name": "John Doe",
"connections": {
"google": {}
}
}'

2. Check Authorization Status

curl -H "X-JWT-Token: eyJ..." \
http://localhost:3007/oauth/google/status

3. Authorize (if needed)

Open in browser:

http://localhost:4002/oauth/google/authorize?tenant_id=acme&user_email=user@example.com

4. Use Google Services

# List calendar events
curl -H "X-JWT-Token: eyJ..." \
"http://localhost:3007/google/calendar/events?time_min=2026-01-12T00:00:00Z"

# List Gmail threads
curl -H "X-JWT-Token: eyJ..." \
"http://localhost:3007/google/gmail/threads?query=is:unread"

# List Chat spaces
curl -H "X-JWT-Token: eyJ..." \
http://localhost:3007/google/chat/spaces

MCP Tool Integration

Google services are not yet integrated into the MCP tool registry. To add Google tools to MCP:

  1. Update ontology.yaml to include Google concepts and operations
  2. Register tools in the MCP tool registry
  3. Add Google domain to the MCP domains list

This will enable AI assistants to discover and use Google services through the MCP protocol.