Quickstart: Add auth to your AI agents
Set up connectors and get OAuth tokens and call tools on the fly using Scalekit's Agent Auth
Users ask your app in natural language - “Show me last 5 unread emails” or “Create a calendar event for tomorrow” or “Send an alert to my team”
All of these actions require your app to be able to securely connect to third-party applications like Gmail, Calendar, Slack, Notion etc. and execute actions on behalf of the user. Scalekit handles:
- Authorizing your application with 3rd party apps.
- Allows you to configure a connection with each 3rd party app using your own credentials or using Scalekit’s credentials (for faster development).
- Every connection would maintain token vault allowing you to fetch tokens and use them to make API calls.
- Provides a unified API to call tools on behalf of the user. Scalekit provides tools if your preferred connector is not supported.
- Manage each user account per connector through Scalekit dashboard or programmatically.
Get started
Agent that fetches last 5 unread emails
Section titled “Agent that fetches last 5 unread emails”In this quickstart, you’ll build a simple tool-calling program that:
- Authenticates a user with Gmail to authenticate their Gmail account over OAuth 2.0
- Fetches last 5 unread emails from the user’s inbox
-
Set up your environment
Section titled “Set up your environment”Get Your credentials from Scalekit dashboard at app.scalekit.com -> Developers-> Settings -> API Credentials
Install the Scalekit SDK for your preferred language and initialize the client with your API credentials:
pip install scalekit-sdk-pythonnpm install @scalekit-sdk/node@2.2.0-beta.1go get github.com/scalekit/scalekit-sdk-gomvn dependency:get -Dartifact=com.scalekit:scalekit-sdk-java:1.0.0import scalekit.clientimport osfrom dotenv import load_dotenvload_dotenv()scalekit_client = scalekit.client.ScalekitClient(client_id=os.getenv("SCALEKIT_CLIENT_ID"),client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"),env_url=os.getenv("SCALEKIT_ENV_URL"),)actions = scalekit_client.actionsimport { ScalekitClient } from '@scalekit-sdk/node';import 'dotenv/config';const scalekit = new ScalekitClient(process.env.SCALEKIT_ENV_URL,process.env.SCALEKIT_CLIENT_ID,process.env.SCALEKIT_CLIENT_SECRET);const { connectedAccounts, tools } = scalekit; -
Create connected account for the user
Section titled “Create connected account for the user”Connected account is a user’s 3rd party account that is registered in your Scalekit environment. This represents the user’s connection to their Gmail account in this case.
# Create or retrieve the user's connected Gmail accountresponse = actions.get_or_create_connected_account(connection_name="gmail",identifier="user_123" # Replace with your system's unique user ID)connected_account = response.connected_accountprint(f'Connected account created: {connected_account.id}')// Create or retrieve the user's connected Gmail accountconst response = await connectedAccounts.getOrCreateConnectedAccount({connector: 'gmail',identifier: 'user_123', // Replace with your system's unique user ID});const connectedAccount = response.connectedAccount;console.log('Connected account created:', connectedAccount?.id); -
Authenticate the user
Section titled “Authenticate the user”In order to execute any tools on behalf of the user, the user first needs to grant authorization to access their gmail account. Scalekit automatically handles the entire OAuth workflow with the Gmail provider, gets the access token, refreshes the access token periodically based on the refresh token etc.
If the user’s access token is expired, Scalekit will automatically refresh the access token using the refresh token. At any time, you can check the authorization status of the connected account and determine if the user needs to re-authorize the connection.
# Generate authorization link if user hasn't authorized or token is expiredif(connected_account.status != "ACTIVE"):print(f"Gmail is not connected: {connected_account.status}")link_response = actions.get_authorization_link(connection_name="gmail",identifier="user_123")print(f"🔗 click on the link to authorize Gmail", link_response.link)input(f"⎆ Press Enter after authorizing Gmail...")# In production, redirect user to this URL to complete OAuth flow// Generate authorization link if user hasn't authorized or token is expiredif (connectedAccount?.status !== 'ACTIVE') {console.log('gmail is not connected:', connectedAccount?.status);const linkResponse = await connectedAccounts.getMagicLinkForConnectedAccount({connector: 'gmail',identifier: 'user_123',});console.log('🔗 click on the link to authorize gmail', linkResponse.link);// In production, redirect user to this URL to complete OAuth flow} -
Fetch OAuth tokens for the user
Section titled “Fetch OAuth tokens for the user”Now that the user has successfully authorized the Gmail connection, you can fetch the latest access token and refresh token for the user’s connected account anytime you want. Scalekit will keep the token refreshed periodically so that you always have the latest valid access token to make API calls on behalf of the user.
# Retrieve the connected account to extract OAuth tokensresponse = actions.get_connected_account(connection_name="gmail",identifier="user_123")connected_account = response.connected_account# Extract OAuth tokens from authorization detailstokens = connected_account.authorization_details["oauth_token"]access_token = tokens["access_token"]refresh_token = tokens["refresh_token"]# Security Warning: Never log OAuth tokens in production. Token exposure in logs# or aggregators creates credential leakage risks. Use safe redaction,# conditional debug-only logging, or secure secret storage instead.if os.getenv("DEBUG"):print("access token:", access_token)print("refresh token:", refresh_token)const accountResponse = await connectedAccounts.getConnectedAccountByIdentifier({connector: 'gmail',identifier: 'user_123',});// Extract OAuth tokens from authorization detailsconst authDetails = accountResponse?.connectedAccount?.authorizationDetails;const accessToken = (authDetails && authDetails.details?.case === "oauthToken")? authDetails.details.value?.accessToken: undefined;const refreshToken = (authDetails && authDetails.details?.case === "oauthToken")? authDetails.details.value?.refreshToken: undefined;console.log('Connected account:', accountResponse);console.log('accessToken:', accessToken);console.log('refreshToken:', refreshToken);Refer to the respective connector’s API documentation for more details on how to use these tokens and consume their API. Scalekit provides tools for most of the connectors, so you can use them to call the API directly.
-
Call the Gmail API to fetch last 5 unread emails
Section titled “Call the Gmail API to fetch last 5 unread emails”Now that the user is authenticated and you have the access token, you can execute the Gmail API to fetch the last 5 unread emails from the user’s inbox. The example below directly calls the Gmail API. You can use the Gmail SDK or any other HTTP client of your choice to make the API calls.
# Fetch 5 unread emails using Gmail APIheaders = {"Authorization": f"Bearer {access_token}", # Use the OAuth access token}list_url = "https://gmail.googleapis.com/gmail/v1/users/me/messages"params = {"q": "is:unread","maxResults": 5,}list_response = requests.get(list_url, headers=headers, params=params)messages = list_response.json().get("messages", [])print(f"\nFound {len(messages)} unread emails:\n")# Fetch and display details for each emailfor msg in messages:msg_url = f"{list_url}/{msg['id']}"msg_response = requests.get(msg_url, headers=headers, params={"format": "metadata", "metadataHeaders": ["From", "Subject", "Date"]})msg_data = msg_response.json()headers_list = msg_data.get("payload", {}).get("headers", [])subject = next((h["value"] for h in headers_list if h["name"] == "Subject"), "No Subject")sender = next((h["value"] for h in headers_list if h["name"] == "From"), "Unknown")date = next((h["value"] for h in headers_list if h["name"] == "Date"), "Unknown")print(f"From: {sender}")print(f"Subject: {subject}")print(f"Date: {date}")print(f"Snippet: {msg_data.get('snippet', '')}")print("-" * 50)// Fetch 5 unread emails using Gmail APIconst listUrl = 'https://gmail.googleapis.com/gmail/v1/users/me/messages';const params = new URLSearchParams({ q: 'is:unread', maxResults: '5' });const listResponse = await fetch(`${listUrl}?${params}`, {headers: { Authorization: `Bearer ${accessToken}` }, // Use the OAuth access token});const listData = await listResponse.json();const messages = listData.messages ?? [];console.log(`\nFound ${messages.length} unread emails:\n`);// Fetch and display details for each emailfor (const msg of messages) {const msgResponse = await fetch(`${listUrl}/${msg.id}?format=metadata&metadataHeaders=From&metadataHeaders=Subject&metadataHeaders=Date`,{ headers: { Authorization: `Bearer ${accessToken}` } });const msgData = await msgResponse.json();const headersList = msgData.payload?.headers ?? [];const subject = headersList.find((h) => h.name === 'Subject')?.value ?? 'No Subject';const sender = headersList.find((h) => h.name === 'From')?.value ?? 'Unknown';const date = headersList.find((h) => h.name === 'Date')?.value ?? 'Unknown';console.log(`From: ${sender}`);console.log(`Subject: ${subject}`);console.log(`Date: ${date}`);console.log(`Snippet: ${msgData.snippet ?? ''}`);console.log('-'.repeat(50));}
Congratulations!
You’ve successfully implemented a program that executes API calls on behalf of a user using Agent Auth.
This can be extended to any number of connections and Scalekit handles the authentication and token management for you.