Skip to content

OAuth Integration

Universal API provides built-in OAuth support for Google services. This guide explains how to connect your Google account and use OAuth tokens in your actions.

Supported Providers

ProviderServicesStatus
GoogleGmail, Drive, Calendar, SheetsAvailable
MicrosoftOffice 365, OneDriveComing soon
GitHubRepos, Issues, PRsComing soon

Connecting Your Google Account

Via Web UI

  1. Go to universalapi.co/keys
  2. Click "Connect Google Account"
  3. Select the scopes you need:
    • Gmail (send/read emails)
    • Drive (file access)
    • Calendar (event management)
  4. Complete the OAuth flow
  5. Your tokens are securely stored

Via API

bash
# Initiate OAuth flow
curl "https://api.universalapi.co/oauth/google/authorize?userId=your-user-id&redirect_uri=https://universalapi.co/oauth/callback"

This returns a URL to redirect the user to Google's consent screen.

Using OAuth in Actions

Check for Tokens

Always verify the user has connected their account:

python
def handler(event, context):
    if "keys" not in event or "google_oauth" not in event["keys"]:
        return {
            "statusCode": 400,
            "body": {
                "error": "Google account not connected. Please connect at universalapi.co/keys"
            }
        }

    # Continue with OAuth token
    google_oauth = event["keys"]["google_oauth"]
    access_token = google_oauth["keyValue"]

Token Structure

When Google OAuth is connected, your action receives:

python
event["keys"]["google_oauth"] = {
    "keyName": "google_oauth",
    "keyValue": "ya29.xxx...",  # Access token
    "secretKeyName": "refresh_token",
    "secretKeyValue": "1//0xxx...",  # Refresh token
    "additionalFields": {
        "token_type": "Bearer",
        "expires_at": 1736784000000,
        "scope": "https://www.googleapis.com/auth/drive https://mail.google.com/"
    }
}

Automatic Token Refresh

Universal API automatically refreshes tokens when they're about to expire (within 5 minutes). Your action always receives a valid access token.

Example: List Google Drive Files

python
import requests

def handler(event, context):
    # Check for OAuth
    if "keys" not in event or "google_oauth" not in event["keys"]:
        return {
            "statusCode": 400,
            "body": {"error": "Please connect your Google account"}
        }

    # Get token
    access_token = event["keys"]["google_oauth"]["keyValue"]

    # Get parameters
    params = event.get("queryStringParameters", {}) or {}
    max_results = int(params.get("max_results", 10))
    query = params.get("query", "")

    # Call Google Drive API
    response = requests.get(
        "https://www.googleapis.com/drive/v3/files",
        headers={"Authorization": f"Bearer {access_token}"},
        params={
            "pageSize": max_results,
            "q": query,
            "fields": "files(id,name,mimeType,webViewLink,modifiedTime)"
        }
    )

    if response.status_code != 200:
        return {
            "statusCode": response.status_code,
            "body": {"error": response.text}
        }

    return {
        "statusCode": 200,
        "body": {
            "files": response.json().get("files", []),
            "count": len(response.json().get("files", []))
        }
    }

Example: Send Gmail Email

python
import requests
import base64
from email.mime.text import MIMEText

def handler(event, context):
    # Check for OAuth
    if "keys" not in event or "google_oauth" not in event["keys"]:
        return {
            "statusCode": 400,
            "body": {"error": "Please connect your Google account"}
        }

    # Get parameters
    params = event.get("queryStringParameters", {}) or {}
    to = params.get("to")
    subject = params.get("subject")
    body = params.get("body")

    if not all([to, subject, body]):
        return {
            "statusCode": 400,
            "body": {"error": "Missing required parameters: to, subject, body"}
        }

    # Get token
    access_token = event["keys"]["google_oauth"]["keyValue"]

    # Create email
    message = MIMEText(body)
    message["to"] = to
    message["subject"] = subject
    raw = base64.urlsafe_b64encode(message.as_bytes()).decode()

    # Send via Gmail API
    response = requests.post(
        "https://gmail.googleapis.com/gmail/v1/users/me/messages/send",
        headers={
            "Authorization": f"Bearer {access_token}",
            "Content-Type": "application/json"
        },
        json={"raw": raw}
    )

    if response.status_code != 200:
        return {
            "statusCode": response.status_code,
            "body": {"error": response.text}
        }

    return {
        "statusCode": 200,
        "body": {
            "success": True,
            "messageId": response.json().get("id")
        }
    }

OAuth Endpoints

Refresh Token Manually

bash
POST /oauth/google/refresh
json
{
  "refreshToken": "1//0xxx..."
}

Revoke Token

bash
POST /oauth/google/revoke
json
{
  "token": "ya29.xxx..."
}

Specifying Required Keys

When creating an action that requires OAuth, specify it in keysNeeded:

json
{
  "actionName": "List Drive Files",
  "keysNeeded": [
    {
      "name": "google_oauth",
      "description": "Google OAuth token with Drive API access"
    }
  ],
  "sourceCode": "..."
}

This helps users understand what credentials are needed before running the action.

Google OAuth Scopes

ScopePermission
https://www.googleapis.com/auth/driveFull Drive access
https://www.googleapis.com/auth/drive.readonlyRead-only Drive
https://mail.google.com/Full Gmail access
https://www.googleapis.com/auth/gmail.sendSend emails only
https://www.googleapis.com/auth/calendarFull Calendar access

Error Handling

python
def handler(event, context):
    if "keys" not in event or "google_oauth" not in event["keys"]:
        return {
            "statusCode": 401,
            "body": {
                "error": "OAUTH_NOT_CONNECTED",
                "message": "Please connect your Google account at universalapi.co/keys",
                "authUrl": "https://universalapi.co/keys"
            }
        }

    try:
        # Make API call
        response = requests.get(...)

        if response.status_code == 401:
            return {
                "statusCode": 401,
                "body": {
                    "error": "OAUTH_TOKEN_EXPIRED",
                    "message": "Please reconnect your Google account"
                }
            }

        return {"statusCode": 200, "body": response.json()}

    except Exception as e:
        return {
            "statusCode": 500,
            "body": {"error": str(e)}
        }

Next Steps

Universal API - The agentic entry point to the universe of APIs