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.