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 identifieruser_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 tokensneeds_oauth: User needs to authorizeno_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 IDevent_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 IDevent_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 IDevent_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 querylabel_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 IDmessage_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 IDmessage_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 IDmessage_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 IDmember_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:
- Update
ontology.yamlto include Google concepts and operations - Register tools in the MCP tool registry
- Add Google domain to the MCP domains list
This will enable AI assistants to discover and use Google services through the MCP protocol.