AgentKit: Complete AgentKit documentation with connectors, frameworks, and tool calling for AI agents --- # DOCUMENT BOUNDARY --- # Authorization - Overview > Learn about authorization options in Agent Auth, including OAuth flows, permissions, and security best practices. Agents that need to take actions on-behalf-of users in third party applications like gmail, calendar, slack, notion, hubspot etc need to do so in a secure, authorized manner. Scalekit’s Agent Auth solution helps developers build agents to act on-behalf-of users by managing user’s authentication and authorization for those tools. ## Supported Auth Methods [Section titled “Supported Auth Methods”](#supported-auth-methods) Agent Auth supports all the different types of authentication and authorization methods that are adopted by different applications so that you don’t have to worry about handling and managing user authorization tokens. * OAuth 2.0 * API Keys * Bearer Tokens * Custom JWTs ## Authorize a user [Section titled “Authorize a user”](#authorize-a-user) ### Create Connected Account [Section titled “Create Connected Account”](#create-connected-account) Create a connected\_account for a user and an application. In the example below - we show how to create a connected account for a user whose unique identifier is user\_123 and gmail application. ```python 1 # Create a connected account for user if it doesn't exist already 2 response = actions.get_or_create_connected_account( 3 connection_name="gmail", 4 identifier="user_123" 5 ) 6 connected_account = response.connected_account 7 print(f'Connected account created: {connected_account.id}') ``` ### Complete authorization [Section titled “Complete authorization”](#complete-authorization) Next, check the authorization status for this user’s connected account. If authorization status is not ACTIVE, generate a unique one-time magic link and redirect the user to this link. Depending on the application’s authentication type, Scalekit presents the user with appropriate next steps to complete user authorization. * If the application requires OAuth 2.0 based authorization, Scalekit will manage the OAuth 2.0 handshake on your behalf and keeps the user’s access token for subsequent tool calls. * If the application requires API Key based authentication, Scalekit will present them with a form to collect API Keys and other necessary information and stores them securely in an encrypted manner and uses them for subsequent tool calls. ```python 1 # If the user hasn't yet authorized the gmail connection or if the user's access token is expired, generate a link for them to authorize the connection 2 if(connected_account.status != "ACTIVE"): 3 print(f"gmail is not connected: {connected_account.status}") 4 link_response = actions.get_authorization_link( 5 connection_name="gmail", 6 identifier="user_123" 7 ) 8 print(f"🔗click on the link to authorize gmail", link_response.link) 9 10 # In a real app, redirect the user to this URL so that the user can complete the authentication process for their gmail account ``` ### Make Authorized Tool Calls [Section titled “Make Authorized Tool Calls”](#make-authorized-tool-calls) Once the user has successfully authorized the applications, your agent can use our SDK to execute tool calls on behalf of the user. Below is a small example to fetch user’s unread emails using the same connected account details. ```python 1 # Fetch recent emails 2 emails = actions.execute_tool( 3 connected_account_id=connected_account.id, 4 tool='gmail_fetch_mails', 5 tool_input={ 6 'query': 'is:unread', 7 'max_results': 5 8 } 9 ) 10 11 print(f'Recent emails: {emails.result}') ``` ## Next Steps [Section titled “Next Steps”](#next-steps) To make your agentic implementation faster, we have added Scalekit’s credentials for popular third party applications like GMail, Google Calendar, Google Drive etc. For a complete white-labelled experience, you can configure your own oauth credentials. [Bring your own Credentials ](/agentkit/advanced/bring-your-own-oauth) --- # DOCUMENT BOUNDARY --- # Add your own connector > Add custom connectors and extend coverage while keeping authentication and authorization in Scalekit. Add your own connector when the API or MCP server you need is not available in Scalekit’s built-in catalog — custom connectors support any SaaS API, partner system, internal API, or remote MCP server while keeping authentication, authorization, and secure API access in Scalekit. Once the connector is created, you use the same flow as other connectors: create a connection, create or fetch a connected account, authorize the user, and perform tool calling. Custom connectors appear alongside built-in connectors when you create a connection in Scalekit: ![Custom connector shown alongside built-in connectors in the connector selection view](/.netlify/images?url=_astro%2Fcustom-provider-in-catalog.BEwx1iKj.png\&w=2596\&h=1138\&dpl=6a01bf5aba8408000850fe26) ## Why add your own connector [Section titled “Why add your own connector”](#why-add-your-own-connector) Adding your own connector lets you: * Extend beyond the built-in connector catalog without inventing a separate auth stack * Bring unsupported SaaS APIs, partner systems, internal APIs, and remote MCP servers into the same secure access model * Reuse connections, connected accounts, and user authorization instead of building one-off auth plumbing * Keep credential handling, authorization, and governed API access centralized in Scalekit * Move from connector definition to live upstream calls through Tool Proxy (REST) or tool calling (MCP) using the same runtime model as other integrations ## How adding your own connector works [Section titled “How adding your own connector works”](#how-adding-your-own-connector-works) Adding your own connector uses the same model as built-in connectors: 1. Create a connector definition 2. Create a connection in Scalekit Dashboard 3. Create a connected account and authorize the user 4. Call tools — via Tool Proxy (`actions.request()`) for REST API connectors, or via MCP tool calling for MCP connectors Creating the connector definition tells Scalekit how to authenticate to the upstream API or MCP server. After that, connections, connected accounts, user authorization, and the call runtime work the same way as they do for built-in connectors. --- # DOCUMENT BOUNDARY --- # Overview > Learn how AgentKit works: tool calling with pre-built connectors and authentication for AI agents acting on behalf of users. AgentKit gives your AI agents authenticated access to third-party apps: sending emails, reading calendars, creating tickets, querying databases, and more. Your agent calls a tool; Scalekit handles the OAuth flow, token storage, and API call. ## Authentication [Section titled “Authentication”](#authentication) **Connections** are configurations you create once in the Scalekit Dashboard. A connection holds the credentials Scalekit needs to authenticate with a connector (OAuth app credentials, API keys, or service account details). One connection serves all your users. **Connected accounts** are per-user instances of a connection. When a user authorizes, Scalekit creates a connected account that stores their tokens and tracks their auth state. Your agent uses a connected account to act on that specific user’s behalf. Scalekit supports OAuth 2.0, API keys, RSA key pairs, and service accounts across all connectors. ## Tool calling [Section titled “Tool calling”](#tool-calling) **Connectors** are the pre-built integrations your agent can use: Gmail, Slack, Salesforce, Snowflake, GitHub, and many others. Each connector exposes a library of tools ready for your agent to call. **Tools** are connector-specific actions: `gmail_fetch_emails`, `salesforce_create_record`, `slack_send_message`. Scalekit provides the tool schemas and handles the authenticated API call. Your agent passes inputs; Scalekit injects the user’s credentials and returns structured output. ## How they fit together [Section titled “How they fit together”](#how-they-fit-together) You configure connections once. Your users authenticate to create connected accounts. Your agent calls tools; Scalekit handles the rest. ## Works with your framework [Section titled “Works with your framework”](#works-with-your-framework) AgentKit is framework-agnostic. Tool schemas work with any LLM API. Native adapters are available for [LangChain](/agentkit/examples/langchain/), [Google ADK](/agentkit/examples/google-adk/), and [MCP-compatible environments](/agentkit/mcp/configure-mcp-server/). ## Get started [Section titled “Get started”](#get-started) [Quickstart ](/agentkit/quickstart)Build a working agent with authenticated tool calls in minutes. [Configure a connection ](/agentkit/connections)Set up your first connection in the Scalekit Dashboard. [Connectors ](/agentkit/connectors/)Browse the pre-built connectors and their tool libraries. [Examples ](/agentkit/examples/)Full working examples for LangChain, Google ADK, Anthropic, OpenAI, and more. --- # DOCUMENT BOUNDARY --- # Tools Overview > Learn about tools in Agent Auth - the standardized functions that enable you to perform actions across different third-party providers. LLMs today are very powerful reasoning and answering machines but their ability is restricted to data sets that they are trained upon and cannot natively interact with web services or saas applications. Tool Calling or Function Calling is how you extend the capabilities of these models to interact and take actions in third party applications on behalf of the users. For example, if you would like to build an email summarizer agent, there are a few challenges that you need to tackle: 1. How to give agents access to gmail 2. How to authorize these agents access to my gmail account 3. What should be the appropriate input parameters to access gmail based on user context and query Agent Auth product solves these problems by giving you simple abstractions using our SDK to help you give additional capabilities to the agents you are building regardless of the underlying model and agent framework in three simple steps. 1. Use Scalekit SDK to fetch all the appropriate tools 2. Complete user authorization handling in one single line of code 3. Use Scalekit’s optimized tool metadata and pass it to the underlying model for optimal tool selection and input parameters. ## Tool Metadata [Section titled “Tool Metadata”](#tool-metadata) Every tool in Agent Auth follows a consistent structure with a name, description and structured input and output schema. Agentic frameworks like Langchain can work with the underlying LLMs to select the right tool to solve the user’s query based on the tool metadata. ### Sample Tool definition [Section titled “Sample Tool definition”](#sample-tool-definition) ```json 1 { 2 "name": "gmail_send_email", 3 "display_name": "Send Email", 4 "description": "Send an email message to one or more recipients", 5 "provider": "gmail", 6 "category": "communication", 7 "input_schema": { 8 "type": "object", 9 "properties": { 10 "to": { 11 "type": "array", 12 "items": {"type": "string", "format": "email"}, 13 "description": "Email addresses of recipients" 14 }, 15 "subject": { 16 "type": "string", 17 "description": "Email subject line" 18 }, 19 "body": { 20 "type": "string", 21 "description": "Email body content" 22 } 23 }, 24 "required": ["to", "subject", "body"] 25 }, 26 "output_schema": { 27 "type": "object", 28 "properties": { 29 "message_id": { 30 "type": "string", 31 "description": "Unique identifier for the sent message" 32 }, 33 "status": { 34 "type": "string", 35 "enum": ["sent", "queued", "failed"], 36 "description": "Status of the email sending operation" 37 } 38 } 39 } 40 } ``` ## Best practices [Section titled “Best practices”](#best-practices) 1. **Tool Selection:** Even though tools provide additional capabilities to the agents, the real challenge in leveraging underlying LLMs capability to select the right tool to solve the job at hand. And LLMs do a poor job when you throw all the available tools you have at your disposal and ask LLMs to pick the right tool. So, be sure to limit the number of tools that you provide in the context to the LLM so that they do a good job in tool selection and filling in the appropriate input parameters to actually execute a certain action successfully. 2. **Add deterministic overrides in undeterministic workflows:** Because LLMs are unpredictable super machines, do not trust them to reliably execute the same workflow every single time in the exact same manner. If your agent has some deterministic patterns or workflows, use the pre-execution modifiers to always set exact input parameters for a given tool. For example, if your agent always reads only unread emails, create a pre-execution modifier to add `is:unread` to the query input param while fetching emails using gmail\_fetch\_emails tool. 3. **Context Window Awareness:** Similar to the point above, always be conscious of overloading context window of the underlying models. Don’t send the entire tool execution response/output to the underlying model for processing the execution response. Use the post-execution modifiers to select only the required and necessary fields in the tool output response before sending the data to the LLMs. *** Tools are the fundamental building blocks through which you can give real world capabilities for the agents you are building. By understanding how to use them effectively, you can build sophisticated agents that seamlessly connect your application to the tools your users already love. --- # DOCUMENT BOUNDARY --- # AgentKit: Connect my agent to apps > Build a working agent that makes authenticated tool calls on behalf of users, using Gmail as the example connector. ![Architecture diagram: an AI agent connects through Scalekit MCP Gateway with delegated auth, scoped permissions, and tool calls to SaaS apps such as Gmail, Slack, and Salesforce.](/_astro/agentkit.CAuIPwfK.svg) By the end of this guide, you’ll have a working agent that fetches a user’s last 5 unread Gmail messages (authenticated with their real account). Scalekit manages the OAuth flow, token storage, and API proxy so you focus on agent logic. ## Before you start [Section titled “Before you start”](#before-you-start) Complete these steps in the Scalekit dashboard before writing any code: 1. **Create a Scalekit account** at [app.scalekit.com](https://app.scalekit.com). 2. **Configure a Gmail connector** at Dashboard → **AgentKit** > **Connections** > **Create Connection** → select **Gmail**. Create the connection in the dashboard before running any code. Then copy the exact **Connection name** from that connection and use that value in your code. It must match the dashboard exactly, and it is not always the provider slug `gmail`. Gmail is enabled by default in new Scalekit environments. To connect to other services, create a connection for each app under **AgentKit** > **Connections** > **Create Connection**. 3. **Copy your API credentials** at Dashboard → **Developers → Settings → API Credentials**. Save these three values as environment variables: * `SCALEKIT_CLIENT_ID` * `SCALEKIT_CLIENT_SECRET` * `SCALEKIT_ENV_URL` * `GMAIL_CONNECTION_NAME` (copy the exact Connection name from **AgentKit** > **Connections**) ## Build your agent [Section titled “Build your agent”](#build-your-agent) * Using a coding agent Install the Scalekit Auth Stack for your coding agent, complete the browser authorization when prompted, then paste the implementation prompt. The agent scaffolds connected account setup, the OAuth flow, and tool execution. * Claude Code Terminal ```bash claude plugin marketplace add scalekit-inc/claude-code-authstack && claude plugin install agent-auth@scalekit-auth-stack ``` Installing the plugin sets up Scalekit’s MCP server and triggers an OAuth authorization flow in your browser. Complete the authorization before continuing. This gives Claude Code direct access to your Scalekit environment to search docs, manage connections, and check connected account status. Then paste the prompt below. * Codex Terminal ```bash curl -fsSL https://raw.githubusercontent.com/scalekit-inc/codex-authstack/main/install.sh | bash ``` Restart Codex → Plugin Directory → **Scalekit Auth Stack** → install **agent-auth**. If a browser authorization prompt appears, complete the OAuth flow before continuing. Then paste the prompt below. * GitHub Copilot CLI Terminal ```bash copilot plugin marketplace add scalekit-inc/github-copilot-authstack copilot plugin install agent-auth@scalekit-auth-stack ``` If a browser authorization prompt appears, complete the OAuth flow before continuing. Then run: Terminal ```bash copilot "Configure Scalekit agent authentication for Gmail. Provide code to create a connected account, generate an authorization link, and fetch the last 5 unread emails using Scalekit's tool API." ``` * Cursor Terminal ```bash curl -fsSL https://raw.githubusercontent.com/scalekit-inc/cursor-authstack/main/install.sh | bash ``` Reload Cursor → **Settings → Plugins** → enable **Agent Auth**. If a browser authorization prompt appears, complete the OAuth flow before continuing. Open chat (Cmd+L / Ctrl+L) and paste the prompt below. * 40+ agents Terminal ```bash npx skills add scalekit-inc/skills --skill integrating-agent-auth ``` Then ask your agent: “Configure Scalekit agent authentication for Gmail, create a connected account, generate an authorization link, and fetch the last 5 unread emails using Scalekit’s tool API.” Implementation prompt ```md Configure Scalekit agent authentication for Gmail. Provide code to create a connected account, generate an authorization link, and, once the user authorizes, fetch the last 5 unread emails using Scalekit's tool API. ``` Review generated code before deploying Verify that token validation logic, error handling, and environment variable references match your application’s requirements. * Step by step Terminal ```bash claude plugin marketplace add scalekit-inc/claude-code-authstack && claude plugin install agent-auth@scalekit-auth-stack ``` Installing the plugin sets up Scalekit’s MCP server and triggers an OAuth authorization flow in your browser. Complete the authorization before continuing. This gives Claude Code direct access to your Scalekit environment to search docs, manage connections, and check connected account status. Then paste the prompt below. * Claude Code Terminal ```bash curl -fsSL https://raw.githubusercontent.com/scalekit-inc/codex-authstack/main/install.sh | bash ``` Restart Codex → Plugin Directory → **Scalekit Auth Stack** → install **agent-auth**. If a browser authorization prompt appears, complete the OAuth flow before continuing. Then paste the prompt below. * Codex Terminal ```bash copilot plugin marketplace add scalekit-inc/github-copilot-authstack copilot plugin install agent-auth@scalekit-auth-stack ``` If a browser authorization prompt appears, complete the OAuth flow before continuing. Then run: Terminal ```bash copilot "Configure Scalekit agent authentication for Gmail. Provide code to create a connected account, generate an authorization link, and fetch the last 5 unread emails using Scalekit's tool API." ``` * GitHub Copilot CLI Terminal ```bash curl -fsSL https://raw.githubusercontent.com/scalekit-inc/cursor-authstack/main/install.sh | bash ``` Reload Cursor → **Settings → Plugins** → enable **Agent Auth**. If a browser authorization prompt appears, complete the OAuth flow before continuing. Open chat (Cmd+L / Ctrl+L) and paste the prompt below. * Cursor Terminal ```bash npx skills add scalekit-inc/skills --skill integrating-agent-auth ``` Then ask your agent: “Configure Scalekit agent authentication for Gmail, create a connected account, generate an authorization link, and fetch the last 5 unread emails using Scalekit’s tool API.” * 40+ agents ### 1. Set up your environment [Section titled “1. Set up your environment”](#1-set-up-your-environment) Install the Scalekit SDK and initialize the client with your API credentials: * Python ```sh pip install scalekit-sdk-python python-dotenv requests ``` * Node.js ```sh npm install @scalekit-sdk/node ``` - Python ```python import scalekit.client import os import requests from dotenv import load_dotenv load_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.actions connection_name = os.getenv("GMAIL_CONNECTION_NAME") # must match the Connection name in the dashboard exactly ``` - Node.js ```typescript import { ScalekitClient } from '@scalekit-sdk/node'; import { ConnectorStatus } from '@scalekit-sdk/node/lib/pkg/grpc/scalekit/v1/connected_accounts/connected_accounts_pb'; import 'dotenv/config'; const scalekit = new ScalekitClient( process.env.SCALEKIT_ENV_URL, process.env.SCALEKIT_CLIENT_ID, process.env.SCALEKIT_CLIENT_SECRET ); const actions = scalekit.actions; const connectionName = process.env.GMAIL_CONNECTION_NAME!; // must match the Connection name in the dashboard exactly ``` ### 2. Create a connected account [Section titled “2. Create a connected account”](#2-create-a-connected-account) Scalekit tracks each user’s third-party connection as a connected account. This is the record that holds their OAuth tokens. Creating it tells Scalekit to start managing the user’s Gmail access on your behalf. This step fails if the Gmail connection has not been created in **AgentKit** > **Connections** yet, or if `connection_name` / `connectionName` does not match the dashboard exactly. * Python ```python # Create or retrieve the user's connected Gmail account response = actions.get_or_create_connected_account( connection_name=connection_name, identifier="user_123" # Replace with your system's unique user ID ) connected_account = response.connected_account print(f'Connected account created: {connected_account.id}') ``` * Node.js ```typescript // Create or retrieve the user's connected Gmail account const response = await actions.getOrCreateConnectedAccount({ connectionName, identifier: 'user_123', // Replace with your system's unique user ID }); const connectedAccount = response.connectedAccount; console.log('Connected account created:', connectedAccount?.id); ``` ### 3. Authenticate the user [Section titled “3. Authenticate the user”](#3-authenticate-the-user) Your agent can’t act on behalf of a user until they authorize access. Generate an authorization link, send it to the user, and Scalekit handles the rest: token exchange, storage, and automatic refresh. Once they complete the flow, the connected account status becomes `ACTIVE`. * Python ```python # Generate authorization link if user hasn't authorized or token is expired if(connected_account.status != "ACTIVE"): print(f"Gmail is not connected: {connected_account.status}") link_response = actions.get_authorization_link( connection_name=connection_name, 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 ``` * Node.js ```typescript // Generate authorization link if user hasn't authorized or token is expired if (connectedAccount?.status !== ConnectorStatus.ACTIVE) { console.log('gmail is not connected:', connectedAccount?.status); const linkResponse = await actions.getAuthorizationLink({ connectionName, 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 } ``` Open the link in a browser and authorize the Gmail connection. Once complete, the connected account status updates to `ACTIVE` and your agent can act on the user’s behalf. ### 4. Fetch emails via tool call [Section titled “4. Fetch emails via tool call”](#4-fetch-emails-via-tool-call) Pass the tool name and your inputs to Scalekit. It handles the request to Gmail and returns a structured response your agent can reason over directly: no endpoint URLs, auth headers, or response parsing required. * Python ```python response = actions.execute_tool( tool_name="gmail_fetch_mails", identifier="user_123", tool_input={ "query": "is:unread", "max_results": 5, }, ) print(response) ``` * Node.js ```typescript const toolResponse = await actions.executeTool({ toolName: 'gmail_fetch_mails', connectedAccountId: connectedAccount?.id, toolInput: { query: 'is:unread', max_results: 5, }, }); console.log('Recent emails:', toolResponse.data); ``` * Python ```sh pip install scalekit-sdk-python python-dotenv requests ``` * Node.js ```sh npm install @scalekit-sdk/node ``` * Python ```python import scalekit.client import os import requests from dotenv import load_dotenv load_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.actions connection_name = os.getenv("GMAIL_CONNECTION_NAME") # must match the Connection name in the dashboard exactly ``` * Node.js ```typescript import { ScalekitClient } from '@scalekit-sdk/node'; import { ConnectorStatus } from '@scalekit-sdk/node/lib/pkg/grpc/scalekit/v1/connected_accounts/connected_accounts_pb'; import 'dotenv/config'; const scalekit = new ScalekitClient( process.env.SCALEKIT_ENV_URL, process.env.SCALEKIT_CLIENT_ID, process.env.SCALEKIT_CLIENT_SECRET ); const actions = scalekit.actions; const connectionName = process.env.GMAIL_CONNECTION_NAME!; // must match the Connection name in the dashboard exactly ``` * Python ```python # Create or retrieve the user's connected Gmail account response = actions.get_or_create_connected_account( connection_name=connection_name, identifier="user_123" # Replace with your system's unique user ID ) connected_account = response.connected_account print(f'Connected account created: {connected_account.id}') ``` * Node.js ```typescript // Create or retrieve the user's connected Gmail account const response = await actions.getOrCreateConnectedAccount({ connectionName, identifier: 'user_123', // Replace with your system's unique user ID }); const connectedAccount = response.connectedAccount; console.log('Connected account created:', connectedAccount?.id); ``` * Python ```python # Generate authorization link if user hasn't authorized or token is expired if(connected_account.status != "ACTIVE"): print(f"Gmail is not connected: {connected_account.status}") link_response = actions.get_authorization_link( connection_name=connection_name, 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 ``` * Node.js ```typescript // Generate authorization link if user hasn't authorized or token is expired if (connectedAccount?.status !== ConnectorStatus.ACTIVE) { console.log('gmail is not connected:', connectedAccount?.status); const linkResponse = await actions.getAuthorizationLink({ connectionName, 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 } ``` * Python ```python response = actions.execute_tool( tool_name="gmail_fetch_mails", identifier="user_123", tool_input={ "query": "is:unread", "max_results": 5, }, ) print(response) ``` * Node.js ```typescript const toolResponse = await actions.executeTool({ toolName: 'gmail_fetch_mails', connectedAccountId: connectedAccount?.id, toolInput: { query: 'is:unread', max_results: 5, }, }); console.log('Recent emails:', toolResponse.data); ``` ## Verify it works [Section titled “Verify it works”](#verify-it-works) Run your agent and confirm: * The connected account status is `ACTIVE` after the user completes the Gmail OAuth flow. * The tool response contains structured email data (subject, sender, snippet, and timestamp) ready for your agent to process. If the connected account stays in a `non-ACTIVE` state, the user has not completed the OAuth flow. Regenerate the authorization link and try again. ## Next steps [Section titled “Next steps”](#next-steps) * [Secure user verification](/agentkit/user-verification/): Confirm the OAuth identity matches your logged-in user before activating a connected account. Required for production. * [Connected accounts](/agentkit/connected-accounts/): Manage user connections across multiple providers. * [Tool calling](/agentkit/tools/scalekit-optimized-tools/): Use Scalekit’s optimized tools to call APIs without managing endpoints yourself. --- # DOCUMENT BOUNDARY --- # AgentKit code samples > Full working examples showing how to integrate AgentKit with popular AI frameworks and agent platforms. Each example builds a working agent that reads a user’s Gmail inbox using Scalekit-authenticated tools. ## No agent loop to build [Section titled “No agent loop to build”](#no-agent-loop-to-build) These platforms manage the agent harness for you. Pass a Scalekit MCP URL, describe the task, and the platform handles tool discovery, execution, and session state. [Claude Managed Agents ](/agentkit/examples/claude-managed-agents/)Anthropic runs the agent loop. Pass a Scalekit MCP URL, describe a task, and Claude handles tool discovery, execution, and retries. [OpenClaw ](/agentkit/openclaw/)Conversational agent platform. No code required to connect 50+ services including Gmail, Slack, Notion, and LinkedIn. ## Build your own agent loop [Section titled “Build your own agent loop”](#build-your-own-agent-loop) These integrations give you full control. Fetch Scalekit tool schemas, wire them into your framework, and run the tool-use loop yourself. | Framework | Language | Integration | Notes | | ---------------------------------------------- | --------------- | -------------------- | -------------------------------------------------------------------------------- | | [LangChain](/agentkit/examples/langchain/) | Python | SDK, native adapter | Scalekit returns native LangChain tool objects. No schema reshaping needed. | | [Google ADK](/agentkit/examples/google-adk/) | Python | SDK, native adapter | Scalekit returns native ADK tool objects. No schema reshaping needed. | | [Anthropic](/agentkit/examples/anthropic/) | Python, Node.js | SDK, direct | Tool schemas use `input_schema`, which matches Anthropic’s format exactly. | | [OpenAI](/agentkit/examples/openai/) | Python, Node.js | SDK, direct | Rename `input_schema` to `parameters` to match OpenAI’s function format. | | [Vercel AI SDK](/agentkit/examples/vercel-ai/) | Node.js | SDK, `tool()` helper | Wrap tools with `tool()` and `jsonSchema()`. No manual schema conversion needed. | | [Mastra](/agentkit/examples/mastra/) | Node.js | MCP | Native MCP support via `@mastra/mcp`. Tool discovery is automatic. | ## Working examples on GitHub [Section titled “Working examples on GitHub”](#working-examples-on-github) ### [Connect LangChain agents to Gmail](https://github.com/scalekit-inc/sample-langchain-agent) [Securely connect a LangChain agent to Gmail using Scalekit for authentication. Python example for tool authorization.](https://github.com/scalekit-inc/sample-langchain-agent) ### [Connect Google GenAI agents to Gmail](https://github.com/scalekit-inc/google-adk-agent-example) [Build a Google ADK agent that securely accesses Gmail tools. Python example demonstrating Scalekit auth integration.](https://github.com/scalekit-inc/google-adk-agent-example) ### [Connect agents to Slack tools](https://github.com/scalekit-inc/python-connect-demos/tree/main/direct) [Authorize Python agents to use Slack tools with Scalekit. Direct integration example for secure tool access.](https://github.com/scalekit-inc/python-connect-demos/tree/main/direct) ### [Browse all agent auth examples](https://github.com/scalekit-developers/agent-auth-examples) [A curated collection of working examples showing how to build agents that authenticate and access tools using Scalekit.](https://github.com/scalekit-developers/agent-auth-examples) --- # DOCUMENT BOUNDARY --- # Anthropic > Build an Anthropic agent with Scalekit-authenticated tools. Scalekit returns tool schemas in Anthropic's native format; no conversion needed. Build an agent using Anthropic’s Claude that reads a user’s Gmail inbox. Scalekit returns tool schemas with `input_schema`, the exact format Anthropic’s tool use API expects. ## Install [Section titled “Install”](#install) * Python ```sh 1 pip install scalekit-sdk-python anthropic ``` * Node.js ```sh 1 npm install @scalekit-sdk/node @anthropic-ai/sdk ``` ## Initialize [Section titled “Initialize”](#initialize) * Python ```python 1 import os 2 import scalekit.client 3 import anthropic 4 from google.protobuf.json_format import MessageToDict 5 6 scalekit_client = scalekit.client.ScalekitClient( 7 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 8 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 9 env_url=os.getenv("SCALEKIT_ENV_URL"), 10 ) 11 actions = scalekit_client.actions 12 client = anthropic.Anthropic() ``` * Node.js ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node'; 2 import { ConnectorStatus } from '@scalekit-sdk/node/lib/pkg/grpc/scalekit/v1/connected_accounts/connected_accounts_pb'; 3 import Anthropic from '@anthropic-ai/sdk'; 4 5 const scalekit = new ScalekitClient( 6 process.env.SCALEKIT_ENV_URL!, 7 process.env.SCALEKIT_CLIENT_ID!, 8 process.env.SCALEKIT_CLIENT_SECRET!, 9 ); 10 const anthropic = new Anthropic(); ``` ## Connect the user to Gmail [Section titled “Connect the user to Gmail”](#connect-the-user-to-gmail) * Python ```python 1 response = actions.get_or_create_connected_account( 2 connection_name="gmail", 3 identifier="user_123", 4 ) 5 if response.connected_account.status != "ACTIVE": 6 link = actions.get_authorization_link(connection_name="gmail", identifier="user_123") 7 print("Authorize Gmail:", link.link) 8 input("Press Enter after authorizing...") ``` * Node.js ```typescript 1 const { connectedAccount } = await scalekit.actions.getOrCreateConnectedAccount({ 2 connectionName: 'gmail', 3 identifier: 'user_123', 4 }); 5 if (connectedAccount?.status !== ConnectorStatus.ACTIVE) { 6 const { link } = await scalekit.actions.getAuthorizationLink({ connectionName: 'gmail', identifier: 'user_123' }); 7 console.log('Authorize Gmail:', link); 8 } ``` See [Authorize a user](/agentkit/tools/authorize/) for production auth handling. ## Run the agent [Section titled “Run the agent”](#run-the-agent) Fetch tools scoped to this user, then run the full Claude tool-use loop: * Python ```python 1 # Fetch tools scoped to this user 2 scoped_response, _ = actions.tools.list_scoped_tools( 3 identifier="user_123", 4 filter={"connection_names": ["gmail"]}, 5 page_size=100, # fetch beyond the default page so no connector tools are missed 6 ) 7 llm_tools = [ 8 { 9 "name": MessageToDict(t.tool).get("definition", {}).get("name"), 10 "description": MessageToDict(t.tool).get("definition", {}).get("description", ""), 11 "input_schema": MessageToDict(t.tool).get("definition", {}).get("input_schema", {}), 12 } 13 for t in scoped_response.tools 14 ] 15 16 # Run the agent loop 17 messages = [{"role": "user", "content": "Fetch my last 5 unread emails and summarize them"}] 18 19 while True: 20 response = client.messages.create( 21 model="claude-sonnet-4-6", 22 max_tokens=1024, 23 tools=llm_tools, 24 messages=messages, 25 ) 26 if response.stop_reason == "end_turn": 27 print(response.content[0].text) 28 break 29 30 tool_results = [] 31 for block in response.content: 32 if block.type == "tool_use": 33 result = actions.execute_tool( 34 tool_name=block.name, 35 identifier="user_123", 36 tool_input=block.input, 37 ) 38 tool_results.append({ 39 "type": "tool_result", 40 "tool_use_id": block.id, 41 "content": str(result.data), 42 }) 43 44 messages.append({"role": "assistant", "content": response.content}) 45 messages.append({"role": "user", "content": tool_results}) ``` * Node.js ```typescript 1 // Fetch tools scoped to this user 2 const { tools } = await scalekit.tools.listScopedTools('user_123', { 3 filter: { connectionNames: ['gmail'] }, 4 pageSize: 100, // fetch beyond the default page so no connector tools are missed 5 }); 6 const llmTools = tools.map(t => ({ 7 name: t.tool.definition.name, 8 description: t.tool.definition.description, 9 input_schema: t.tool.definition.input_schema, 10 })); 11 12 // Run the agent loop 13 const messages: Anthropic.MessageParam[] = [ 14 { role: 'user', content: 'Fetch my last 5 unread emails and summarize them' }, 15 ]; 16 17 while (true) { 18 const response = await anthropic.messages.create({ 19 model: 'claude-sonnet-4-6', 20 max_tokens: 1024, 21 tools: llmTools, 22 messages, 23 }); 24 25 if (response.stop_reason === 'end_turn') { 26 const text = response.content.find(b => b.type === 'text'); 27 if (text?.type === 'text') console.log(text.text); 28 break; 29 } 30 31 const toolResults: Anthropic.ToolResultBlockParam[] = []; 32 for (const block of response.content) { 33 if (block.type === 'tool_use') { 34 const result = await scalekit.actions.executeTool({ 35 toolName: block.name, 36 identifier: 'user_123', 37 toolInput: block.input as Record, 38 }); 39 toolResults.push({ type: 'tool_result', tool_use_id: block.id, content: JSON.stringify(result.data) }); 40 } 41 } 42 messages.push({ role: 'assistant', content: response.content }); 43 messages.push({ role: 'user', content: toolResults }); 44 } ``` ## Use MCP instead [Section titled “Use MCP instead”](#use-mcp-instead) Claude Desktop and other Anthropic-compatible MCP hosts connect directly to Scalekit MCP URLs. Add the URL to your MCP host config: ```json 1 { 2 "mcpServers": { 3 "scalekit": { 4 "transport": "streamable-http", 5 "url": "your-scalekit-mcp-url" 6 } 7 } 8 } ``` For programmatic use, connect via any MCP client library and pass tools to `anthropic.messages.create`. See [Connect an MCP client](/agentkit/mcp/connect-mcp-client/) for setup details and [Generate user MCP URLs](/agentkit/mcp/generate-user-urls/) to get the URL. --- # DOCUMENT BOUNDARY --- # Claude Managed Agents > Connect a Claude Managed Agent to Scalekit-authenticated tools via MCP. Anthropic runs the agent loop; you describe the task, Claude handles the rest. Beta Claude Managed Agents is in public beta. All API requests require the `managed-agents-2026-04-01` beta header, which the Anthropic SDK sets automatically. API behavior may change between releases. Connect a Claude Managed Agent to Scalekit-authenticated Gmail tools via MCP. You generate a Scalekit MCP URL, pass it to the agent, and describe a task. Anthropic manages the agent loop: tool discovery, execution, retries, and session state. Compare this to the [Anthropic SDK example](/agentkit/examples/anthropic/): that approach uses the Messages API and requires you to fetch tool schemas, build a tool-use loop, and feed results back manually. Here, none of that exists in your code. ## Prerequisites [Section titled “Prerequisites”](#prerequisites) * A Scalekit account with a Gmail connection configured. See [Configure a connection](/agentkit/connections/). * A Scalekit MCP config and a per-user instance URL already created. See [Configure an MCP server](/agentkit/mcp/configure-mcp-server/) and [Generate user MCP URLs](/agentkit/mcp/generate-user-urls/). * An [Anthropic API key](https://platform.anthropic.com/settings/keys). ## Install [Section titled “Install”](#install) ```sh 1 pip install anthropic scalekit-sdk-python ``` ## Get a Scalekit MCP URL [Section titled “Get a Scalekit MCP URL”](#get-a-scalekit-mcp-url) Generate a per-user MCP URL from your existing MCP config. This URL is pre-authenticated; it encodes the user’s identity and their authorized connections. ```python 1 import os 2 import scalekit.client 3 4 scalekit_client = scalekit.client.ScalekitClient( 5 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 6 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 ) 9 actions = scalekit_client.actions 10 11 inst_response = actions.mcp.ensure_instance( 12 config_name="email-assistant", # your MCP config name 13 user_identifier="user_123", # your app's unique user ID 14 ) 15 mcp_url = inst_response.instance.url ``` Keep the MCP URL server-side The MCP URL is pre-authenticated. Never expose it in client-side code or browser requests. Pass it directly to the Managed Agent from your backend. Before passing the URL to the agent, confirm the user has authorized all connections the config requires. See [Check auth state](/agentkit/mcp/generate-user-urls/#check-auth-state). ## Create the agent [Section titled “Create the agent”](#create-the-agent) Define the agent once and reuse it across sessions. Pass the Scalekit MCP URL as an MCP server; no vault or additional auth configuration needed, because the URL is already authenticated. ```python 1 from anthropic import Anthropic 2 3 client = Anthropic() 4 5 agent = client.beta.agents.create( 6 name="Gmail Assistant", 7 model="claude-opus-4-7", 8 system="You are a helpful assistant with access to the user's Gmail account.", 9 mcp_servers=[ 10 { 11 "type": "url", 12 "name": "scalekit", 13 "url": mcp_url, 14 }, 15 ], 16 tools=[ 17 { 18 "type": "mcp_toolset", 19 "mcp_server_name": "scalekit", 20 "default_config": {"permission_policy": {"type": "always_allow"}}, 21 }, 22 ], 23 ) ``` Save `agent.id`. You reference it in every session; no need to recreate the agent per user. ## Create an environment [Section titled “Create an environment”](#create-an-environment) An environment is the cloud container the agent runs in. Create one and reuse it. ```python 1 environment = client.beta.environments.create( 2 name="gmail-agent-env", 3 config={ 4 "type": "cloud", 5 "networking": {"type": "unrestricted"}, 6 }, 7 ) ``` ## Run a session [Section titled “Run a session”](#run-a-session) Start a session, send a task, and stream results. No `vault_ids` needed; the Scalekit MCP URL handles authentication. ```python 1 session = client.beta.sessions.create( 2 agent=agent.id, 3 environment_id=environment.id, 4 title="Gmail session", 5 ) 6 7 with client.beta.sessions.events.stream(session.id) as stream: 8 client.beta.sessions.events.send( 9 session.id, 10 events=[ 11 { 12 "type": "user.message", 13 "content": [ 14 { 15 "type": "text", 16 "text": "Fetch my last 5 unread emails and summarize them.", 17 } 18 ], 19 }, 20 ], 21 ) 22 23 for event in stream: 24 match event.type: 25 case "agent.message": 26 for block in event.content: 27 print(block.text, end="") 28 case "agent.tool_use": 29 print(f"\n[{event.name}]") 30 case "session.status_idle": 31 print("\n") 32 break ``` The agent discovers available Gmail tools from the Scalekit MCP server, executes them using the user’s pre-authorized credentials, and streams results back. You don’t manage any of that loop. --- # DOCUMENT BOUNDARY --- # Google ADK > Build a Google ADK agent with Scalekit-authenticated Gmail tools. Scalekit returns native ADK tool objects; no schema reshaping needed. Build a Google ADK agent that reads a user’s Gmail inbox. Scalekit handles OAuth, token storage, and returns tools as native ADK tool objects compatible with any ADK agent. [Full code on GitHub ](https://github.com/scalekit-inc/google-adk-agent-example) ## Install [Section titled “Install”](#install) ```sh 1 pip install scalekit-sdk-python google-adk ``` ## Initialize [Section titled “Initialize”](#initialize) ```python 1 import os 2 import asyncio 3 import scalekit.client 4 5 scalekit_client = scalekit.client.ScalekitClient( 6 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 7 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 8 env_url=os.getenv("SCALEKIT_ENV_URL"), 9 ) 10 actions = scalekit_client.actions ``` ## Connect the user to Gmail [Section titled “Connect the user to Gmail”](#connect-the-user-to-gmail) ```python 1 response = actions.get_or_create_connected_account( 2 connection_name="gmail", 3 identifier="user_123", 4 ) 5 if response.connected_account.status != "ACTIVE": 6 link = actions.get_authorization_link(connection_name="gmail", identifier="user_123") 7 print("Authorize Gmail:", link.link) 8 input("Press Enter after authorizing...") ``` See [Authorize a user](/agentkit/tools/authorize/) for production auth handling. ## Build and run the agent [Section titled “Build and run the agent”](#build-and-run-the-agent) `actions.google.get_tools()` returns native ADK tool objects. Pass them directly to a Google ADK `Agent`: ```python 1 from google.adk.agents import Agent 2 from google.adk.runners import Runner 3 from google.adk.sessions import InMemorySessionService 4 from google.genai import types 5 6 tools = actions.google.get_tools( 7 identifier="user_123", 8 connection_names=["gmail"], 9 page_size=100, # avoid missing tools when a connector has more than the default page 10 ) 11 12 agent = Agent( 13 name="gmail_assistant", 14 model="gemini-2.0-flash", 15 instruction="You are a helpful Gmail assistant.", 16 tools=tools, 17 ) 18 19 async def main(): 20 session_service = InMemorySessionService() 21 runner = Runner(agent=agent, app_name="gmail_app", session_service=session_service) 22 session = await session_service.create_session(app_name="gmail_app", user_id="user_123") 23 24 message = types.Content( 25 role="user", 26 parts=[types.Part(text="Fetch my last 5 unread emails and summarize them")], 27 ) 28 async for event in runner.run_async( 29 user_id="user_123", 30 session_id=session.id, 31 new_message=message, 32 ): 33 if event.is_final_response(): 34 print(event.response.text) 35 36 asyncio.run(main()) ``` ## Use MCP instead [Section titled “Use MCP instead”](#use-mcp-instead) Google ADK supports MCP via `MCPToolset`. Connect to a Scalekit-generated MCP URL to skip tool setup: ```python 1 from google.adk.agents import Agent 2 from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset, StreamableHTTPConnectionParams 3 4 agent = Agent( 5 name="gmail_assistant", 6 model="gemini-2.0-flash", 7 instruction="You are a helpful Gmail assistant.", 8 tools=[ 9 MCPToolset( 10 connection_params=StreamableHTTPConnectionParams(url=mcp_url) 11 ) 12 ], 13 ) ``` See [Generate user MCP URLs](/agentkit/mcp/generate-user-urls/) to get `mcp_url`. --- # DOCUMENT BOUNDARY --- # LangChain > Build a LangChain agent with Scalekit-authenticated Gmail tools. Scalekit returns native LangChain tool objects; no schema reshaping needed. Build a LangChain agent that reads a user’s Gmail inbox. Scalekit handles OAuth, token storage, and returns tools in native LangChain format. Your agent code needs no Scalekit-specific logic beyond initialization. [Full code on GitHub ](https://github.com/scalekit-inc/sample-langchain-agent) ## Install [Section titled “Install”](#install) ```sh 1 pip install scalekit-sdk-python langchain-openai ``` ## Initialize [Section titled “Initialize”](#initialize) ```python 1 import os 2 import scalekit.client 3 4 scalekit_client = scalekit.client.ScalekitClient( 5 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 6 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 ) 9 actions = scalekit_client.actions ``` ## Connect the user to Gmail [Section titled “Connect the user to Gmail”](#connect-the-user-to-gmail) ```python 1 response = actions.get_or_create_connected_account( 2 connection_name="gmail", 3 identifier="user_123", 4 ) 5 if response.connected_account.status != "ACTIVE": 6 link = actions.get_authorization_link(connection_name="gmail", identifier="user_123") 7 print("Authorize Gmail:", link.link) 8 input("Press Enter after authorizing...") ``` See [Authorize a user](/agentkit/tools/authorize/) for production auth handling. ## Build and run the agent [Section titled “Build and run the agent”](#build-and-run-the-agent) `actions.langchain.get_tools()` returns native `StructuredTool` objects. Bind them to your LLM and run the tool-calling loop: ```python 1 from langchain_openai import ChatOpenAI 2 from langchain_core.messages import HumanMessage, ToolMessage 3 4 tools = actions.langchain.get_tools( 5 identifier="user_123", 6 connection_names=["gmail"], 7 page_size=100, # avoid missing tools when a connector has more than the default page 8 ) 9 tool_map = {t.name: t for t in tools} 10 11 llm = ChatOpenAI(model="gpt-4o").bind_tools(tools) 12 messages = [HumanMessage("Fetch my last 5 unread emails and summarize them")] 13 14 while True: 15 response = llm.invoke(messages) 16 messages.append(response) 17 if not response.tool_calls: 18 print(response.content) 19 break 20 for tc in response.tool_calls: 21 result = tool_map[tc["name"]].invoke(tc["args"]) 22 messages.append(ToolMessage(content=str(result), tool_call_id=tc["id"])) ``` ## Use MCP instead [Section titled “Use MCP instead”](#use-mcp-instead) LangChain supports MCP via `langchain-mcp-adapters`. Install it, then connect to a Scalekit-generated MCP URL: ```sh 1 pip install langchain-mcp-adapters ``` ```python 1 import asyncio 2 from langchain_mcp_adapters.client import MultiServerMCPClient 3 from langchain_openai import ChatOpenAI 4 from langchain_core.messages import HumanMessage, ToolMessage 5 6 async def run(mcp_url: str): 7 async with MultiServerMCPClient( 8 {"scalekit": {"transport": "streamable_http", "url": mcp_url}} 9 ) as client: 10 tools = client.get_tools() 11 tool_map = {t.name: t for t in tools} 12 llm = ChatOpenAI(model="gpt-4o").bind_tools(tools) 13 messages = [HumanMessage("Fetch my last 5 unread emails and summarize them")] 14 15 while True: 16 response = await llm.ainvoke(messages) 17 messages.append(response) 18 if not response.tool_calls: 19 print(response.content) 20 break 21 for tc in response.tool_calls: 22 result = await tool_map[tc["name"]].ainvoke(tc["args"]) 23 messages.append(ToolMessage(content=str(result), tool_call_id=tc["id"])) 24 25 asyncio.run(run(mcp_url)) ``` See [Generate user MCP URLs](/agentkit/mcp/generate-user-urls/) to get `mcp_url`. --- # DOCUMENT BOUNDARY --- # Mastra > Connect a Mastra agent to Scalekit-authenticated tools using MCP. Mastra's native MCP client connects directly to a Scalekit-generated MCP URL. Connect a Mastra agent to Scalekit tools using MCP. Mastra has native MCP support via `@mastra/mcp`. Pass a Scalekit-generated URL and Mastra handles tool discovery automatically. ## Install [Section titled “Install”](#install) ```sh 1 npm install @scalekit-sdk/node @mastra/core @mastra/mcp @ai-sdk/openai ``` ## Get a per-user MCP URL [Section titled “Get a per-user MCP URL”](#get-a-per-user-mcp-url) Generate a Scalekit MCP URL for the user. This requires the Python SDK. Call this from your backend and pass the URL to your Mastra application: ```python 1 # Backend (Python): generate once per user session 2 inst_response = actions.mcp.ensure_instance( 3 config_name="your-mcp-config", 4 user_identifier="user_123", 5 ) 6 mcp_url = inst_response.instance.url 7 # Pass mcp_url to your Mastra app (e.g. via environment variable or API response) ``` See [Configure an MCP server](/agentkit/mcp/configure-mcp-server/) and [Generate user MCP URLs](/agentkit/mcp/generate-user-urls/) to set up the config and generate the URL. ## Build the agent [Section titled “Build the agent”](#build-the-agent) Pass the MCP URL to `MCPClient`. Mastra fetches the tool list and schemas automatically: ```typescript 1 import { Agent } from '@mastra/core/agent'; 2 import { MCPClient } from '@mastra/mcp'; 3 import { openai } from '@ai-sdk/openai'; 4 5 const mcpUrl = process.env.SCALEKIT_MCP_URL!; // set from your backend 6 7 const mcp = new MCPClient({ 8 servers: { 9 scalekit: { url: new URL(mcpUrl) }, 10 }, 11 }); 12 13 const tools = await mcp.getTools(); 14 15 const agent = new Agent({ 16 name: 'gmail_assistant', 17 instructions: 'You are a helpful Gmail assistant.', 18 model: openai('gpt-4o'), 19 tools, 20 }); 21 22 const result = await agent.generate('Fetch my last 5 unread emails and summarize them'); 23 console.log(result.text); 24 25 await mcp.disconnect(); ``` --- # DOCUMENT BOUNDARY --- # OpenAI > Build an OpenAI agent with Scalekit-authenticated tools. Convert Scalekit's tool schemas to OpenAI's function calling format in one step. Build an agent using OpenAI’s GPT models that reads a user’s Gmail inbox. Scalekit’s tool schemas use `input_schema`: rename it to `parameters` and wrap it in OpenAI’s function format. ## Install [Section titled “Install”](#install) * Python ```sh 1 pip install scalekit-sdk-python openai ``` * Node.js ```sh 1 npm install @scalekit-sdk/node openai ``` ## Initialize [Section titled “Initialize”](#initialize) * Python ```python 1 import os, json 2 import scalekit.client 3 from openai import OpenAI 4 from google.protobuf.json_format import MessageToDict 5 6 scalekit_client = scalekit.client.ScalekitClient( 7 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 8 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 9 env_url=os.getenv("SCALEKIT_ENV_URL"), 10 ) 11 actions = scalekit_client.actions 12 client = OpenAI() ``` * Node.js ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node'; 2 import { ConnectorStatus } from '@scalekit-sdk/node/lib/pkg/grpc/scalekit/v1/connected_accounts/connected_accounts_pb'; 3 import OpenAI from 'openai'; 4 5 const scalekit = new ScalekitClient( 6 process.env.SCALEKIT_ENV_URL!, 7 process.env.SCALEKIT_CLIENT_ID!, 8 process.env.SCALEKIT_CLIENT_SECRET!, 9 ); 10 const openai = new OpenAI(); ``` ## Connect the user to Gmail [Section titled “Connect the user to Gmail”](#connect-the-user-to-gmail) * Python ```python 1 response = actions.get_or_create_connected_account( 2 connection_name="gmail", 3 identifier="user_123", 4 ) 5 if response.connected_account.status != "ACTIVE": 6 link = actions.get_authorization_link(connection_name="gmail", identifier="user_123") 7 print("Authorize Gmail:", link.link) 8 input("Press Enter after authorizing...") ``` * Node.js ```typescript 1 const { connectedAccount } = await scalekit.actions.getOrCreateConnectedAccount({ 2 connectionName: 'gmail', 3 identifier: 'user_123', 4 }); 5 if (connectedAccount?.status !== ConnectorStatus.ACTIVE) { 6 const { link } = await scalekit.actions.getAuthorizationLink({ connectionName: 'gmail', identifier: 'user_123' }); 7 console.log('Authorize Gmail:', link); 8 } ``` See [Authorize a user](/agentkit/tools/authorize/) for production auth handling. ## Run the agent [Section titled “Run the agent”](#run-the-agent) Fetch tools scoped to this user, convert to OpenAI’s function format, then run the tool-calling loop: * Python ```python 1 # Fetch and convert tools to OpenAI format 2 scoped_response, _ = actions.tools.list_scoped_tools( 3 identifier="user_123", 4 filter={"connection_names": ["gmail"]}, 5 page_size=100, # fetch beyond the default page so no connector tools are missed 6 ) 7 llm_tools = [ 8 { 9 "type": "function", 10 "function": { 11 "name": MessageToDict(t.tool).get("definition", {}).get("name"), 12 "description": MessageToDict(t.tool).get("definition", {}).get("description", ""), 13 "parameters": MessageToDict(t.tool).get("definition", {}).get("input_schema", {}), 14 }, 15 } 16 for t in scoped_response.tools 17 ] 18 19 # Run the agent loop 20 messages = [{"role": "user", "content": "Fetch my last 5 unread emails and summarize them"}] 21 22 while True: 23 response = client.chat.completions.create( 24 model="gpt-4o", 25 tools=llm_tools, 26 messages=messages, 27 ) 28 message = response.choices[0].message 29 if not message.tool_calls: 30 print(message.content) 31 break 32 33 messages.append(message) 34 for tc in message.tool_calls: 35 result = actions.execute_tool( 36 tool_name=tc.function.name, 37 identifier="user_123", 38 tool_input=json.loads(tc.function.arguments), 39 ) 40 messages.append({ 41 "role": "tool", 42 "tool_call_id": tc.id, 43 "content": str(result.data), 44 }) ``` * Node.js ```typescript 1 // Fetch and convert tools to OpenAI format 2 const { tools } = await scalekit.tools.listScopedTools('user_123', { 3 filter: { connectionNames: ['gmail'] }, 4 pageSize: 100, // fetch beyond the default page so no connector tools are missed 5 }); 6 const llmTools: OpenAI.ChatCompletionTool[] = tools.map(t => ({ 7 type: 'function', 8 function: { 9 name: t.tool.definition.name, 10 description: t.tool.definition.description, 11 parameters: t.tool.definition.input_schema, 12 }, 13 })); 14 15 // Run the agent loop 16 const messages: OpenAI.ChatCompletionMessageParam[] = [ 17 { role: 'user', content: 'Fetch my last 5 unread emails and summarize them' }, 18 ]; 19 20 while (true) { 21 const response = await openai.chat.completions.create({ 22 model: 'gpt-4o', 23 tools: llmTools, 24 messages, 25 }); 26 const message = response.choices[0].message; 27 if (!message.tool_calls?.length) { 28 console.log(message.content); 29 break; 30 } 31 messages.push(message); 32 for (const tc of message.tool_calls) { 33 const result = await scalekit.actions.executeTool({ 34 toolName: tc.function.name, 35 identifier: 'user_123', 36 toolInput: JSON.parse(tc.function.arguments), 37 }); 38 messages.push({ role: 'tool', tool_call_id: tc.id, content: JSON.stringify(result.data) }); 39 } 40 } ``` ## Use the Responses API [Section titled “Use the Responses API”](#use-the-responses-api) OpenAI’s [Responses API](https://platform.openai.com/docs/api-reference/responses) is a stateful alternative to Chat Completions. Instead of managing conversation history yourself, you pass `previous_response_id` to continue a session. The tool schema format is the same. * Python ```python 1 response = client.responses.create( 2 model="gpt-4o", 3 input="Fetch my last 5 unread emails and summarize them", 4 tools=llm_tools, 5 ) 6 7 while any(item.type == "function_call" for item in response.output): 8 tool_results = [ 9 { 10 "type": "function_call_output", 11 "call_id": item.call_id, 12 "output": str(actions.execute_tool( 13 tool_name=item.name, 14 identifier="user_123", 15 tool_input=json.loads(item.arguments), 16 ).data), 17 } 18 for item in response.output 19 if item.type == "function_call" 20 ] 21 response = client.responses.create( 22 model="gpt-4o", 23 previous_response_id=response.id, 24 input=tool_results, 25 tools=llm_tools, 26 ) 27 28 for item in response.output: 29 if item.type == "message": 30 print(item.content[0].text) ``` * Node.js ```typescript 1 let response = await openai.responses.create({ 2 model: 'gpt-4o', 3 input: 'Fetch my last 5 unread emails and summarize them', 4 tools: llmTools, 5 }); 6 7 while (response.output.some(item => item.type === 'function_call')) { 8 const toolResults = await Promise.all( 9 response.output 10 .filter(item => item.type === 'function_call') 11 .map(async item => { 12 const result = await scalekit.actions.executeTool({ 13 toolName: item.name, 14 identifier: 'user_123', 15 toolInput: JSON.parse(item.arguments), 16 }); 17 return { 18 type: 'function_call_output' as const, 19 call_id: item.call_id, 20 output: JSON.stringify(result.data), 21 }; 22 }) 23 ); 24 response = await openai.responses.create({ 25 model: 'gpt-4o', 26 previous_response_id: response.id, 27 input: toolResults, 28 tools: llmTools, 29 }); 30 } 31 32 const message = response.output.find(item => item.type === 'message'); 33 if (message?.type === 'message') console.log(message.content[0].text); ``` ## Use MCP instead [Section titled “Use MCP instead”](#use-mcp-instead) If you prefer the MCP approach, connect your OpenAI agent via the [Vercel AI SDK + MCP](/agentkit/examples/vercel-ai#use-mcp-instead) or LangChain’s MCP client with a Scalekit-generated URL. See [Connect an MCP client](/agentkit/mcp/connect-mcp-client/) for the URL setup. --- # DOCUMENT BOUNDARY --- # Vercel AI SDK > Build a Vercel AI SDK agent with Scalekit-authenticated tools using the tool() helper and jsonSchema() adapter. Build an agent using the Vercel AI SDK that reads a user’s Gmail inbox. Use `tool()` and `jsonSchema()` from the `ai` package to wrap Scalekit tools. No manual schema conversion needed. ## Install [Section titled “Install”](#install) ```sh 1 npm install @scalekit-sdk/node ai @ai-sdk/openai ``` ## Initialize [Section titled “Initialize”](#initialize) ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node'; 2 import { ConnectorStatus } from '@scalekit-sdk/node/lib/pkg/grpc/scalekit/v1/connected_accounts/connected_accounts_pb'; 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL!, 6 process.env.SCALEKIT_CLIENT_ID!, 7 process.env.SCALEKIT_CLIENT_SECRET!, 8 ); ``` ## Connect the user to Gmail [Section titled “Connect the user to Gmail”](#connect-the-user-to-gmail) ```typescript 1 const { connectedAccount } = await scalekit.actions.getOrCreateConnectedAccount({ 2 connectionName: 'gmail', 3 identifier: 'user_123', 4 }); 5 if (connectedAccount?.status !== ConnectorStatus.ACTIVE) { 6 const { link } = await scalekit.actions.getAuthorizationLink({ connectionName: 'gmail', identifier: 'user_123' }); 7 console.log('Authorize Gmail:', link); 8 } ``` See [Authorize a user](/agentkit/tools/authorize/) for production auth handling. ## Run the agent [Section titled “Run the agent”](#run-the-agent) ```typescript 1 import { generateText, jsonSchema, stepCountIs, tool } from 'ai'; 2 import { openai } from '@ai-sdk/openai'; 3 4 const { tools: scopedTools } = await scalekit.tools.listScopedTools('user_123', { 5 filter: { connectionNames: ['gmail'] }, 6 pageSize: 100, // fetch beyond the default page so no connector tools are missed 7 }); 8 9 const tools = Object.fromEntries( 10 scopedTools.map(t => [ 11 t.tool.definition.name, 12 tool({ 13 description: t.tool.definition.description, 14 parameters: jsonSchema(t.tool.definition.input_schema ?? { type: 'object', properties: {} }), 15 execute: async (args) => { 16 const result = await scalekit.actions.executeTool({ 17 toolName: t.tool.definition.name, 18 identifier: 'user_123', 19 toolInput: args, 20 }); 21 return result.data; 22 }, 23 }), 24 ]), 25 ); 26 27 const { text } = await generateText({ 28 model: openai('gpt-4o'), 29 tools, 30 stopWhen: stepCountIs(5), 31 prompt: 'Fetch my last 5 unread emails and summarize them', 32 }); 33 console.log(text); ``` ## Use MCP instead [Section titled “Use MCP instead”](#use-mcp-instead) The Vercel AI SDK supports MCP via `experimental_createMCPClient`. Pass a Scalekit-generated MCP URL to connect without any tool schema setup: ```typescript 1 import { experimental_createMCPClient, generateText } from 'ai'; 2 import { openai } from '@ai-sdk/openai'; 3 4 const mcpClient = await experimental_createMCPClient({ 5 transport: { 6 type: 'sse', 7 url: mcpUrl, // from actions.mcp.ensure_instance() 8 }, 9 }); 10 11 const tools = await mcpClient.tools(); 12 13 const { text } = await generateText({ 14 model: openai('gpt-4o'), 15 tools, 16 stopWhen: stepCountIs(5), 17 prompt: 'Fetch my last 5 unread emails and summarize them', 18 }); 19 await mcpClient.close(); 20 console.log(text); ``` See [Generate user MCP URLs](/agentkit/mcp/generate-user-urls/) to get `mcpUrl`. --- # DOCUMENT BOUNDARY --- # Add Enterprise SSO to Next.js with Auth.js > Wire Scalekit's OIDC interface into Auth.js to ship per-tenant enterprise SSO in Next.js without touching SAML or IdP-specific code. Enterprise customers don’t want to hand over their employees’ credentials to your app — they want SSO through their own IdP. Auth.js handles sessions well, but it has no concept of per-tenant SAML connections or routing by organization. Scalekit fills that gap: it exposes a single OIDC-compliant endpoint that sits in front of every IdP your customers use. This cookbook wires those two pieces together so your app gets enterprise SSO without writing a line of SAML code. ## The problem [Section titled “The problem”](#the-problem) Adding enterprise SSO to a Next.js app sounds simple until you start building it: * **SAML complexity** — every IdP (Okta, Azure AD, Google Workspace, Ping) uses different metadata, certificate rotation schedules, and attribute mappings. You end up maintaining per-IdP configuration forever. * **Per-tenant routing** — each sign-in attempt needs to resolve to the right connection for that customer. A single `clientId` in Auth.js doesn’t model this. * **Duplicate boilerplate** — Okta setup is not Azure AD setup. You write the integration N times, once per IdP your enterprise customers use. * **Session ownership** — SAML assertions and OIDC tokens are not app sessions. Bridging them correctly (handling expiry, attribute claims, refresh) is error-prone without a clear seam. ## Who needs this [Section titled “Who needs this”](#who-needs-this) This cookbook is for you if: * ✅ You’re building a multi-tenant B2B SaaS app * ✅ You already use Auth.js for session management and want to keep it * ✅ You have enterprise customers who require SSO through their own IdP * ✅ You want to avoid ripping out Auth.js to adopt a fully managed auth platform You **don’t** need this if: * ❌ You’re building a consumer app with no enterprise requirements * ❌ Your app has no concept of organizations or tenants * ❌ You don’t have customers asking for Okta/Azure AD/Google Workspace integration ## The solution [Section titled “The solution”](#the-solution) Scalekit exposes a single OIDC-compliant authorization endpoint. Auth.js treats it like any other OIDC provider and manages the session after the callback. You never write SAML code — Scalekit handles the protocol translation, certificate rotation, and attribute normalization for every IdP your customers connect. The routing params (`connection_id`, `organization_id`, `domain`) let you target the right enterprise connection at sign-in time. ## Implementation [Section titled “Implementation”](#implementation) ### 1. Set up Scalekit [Section titled “1. Set up Scalekit”](#1-set-up-scalekit) Create an environment in the [Scalekit dashboard](https://app.scalekit.com/): 1. Copy your **Issuer URL** (e.g. `https://yourenv.scalekit.dev`), **Client ID** (`skc_...`), and **Client Secret** from **API Keys**. 2. Register your redirect URI: `http://localhost:3000/auth/callback/scalekit` > This guide sets `basePath: "/auth"` in `auth.ts` — a custom override. The Auth.js v5 default is `/api/auth`. Register your redirect URI to match whatever `basePath` you configure or the OAuth flow will fail. 3. Create an **Organization** and add an **SSO Connection** for your test IdP. 4. Copy the **Connection ID** (`conn_...`) — you’ll use it to route sign-in attempts during development. ### 2. Install dependencies [Section titled “2. Install dependencies”](#2-install-dependencies) ```bash 1 pnpm add next-auth ``` Auth.js v5 (`next-auth@5`) ships as a single package. No separate adapter is needed for JWT sessions. ### 3. Add the Scalekit provider [Section titled “3. Add the Scalekit provider”](#3-add-the-scalekit-provider) providers/scalekit.ts ```typescript 1 import type { OAuthConfig, OAuthUserConfig } from "next-auth/providers" 2 3 export interface ScalekitProfile extends Record { 4 sub: string 5 email: string 6 email_verified: boolean 7 name: string 8 given_name: string 9 family_name: string 10 picture: string 11 oid: string // organization_id 12 } 13 14 export default function Scalekit

( 15 options: OAuthUserConfig

& { 16 issuer: string 17 organizationId?: string 18 connectionId?: string 19 domain?: string 20 } 21 ): OAuthConfig

{ 22 const { issuer, organizationId, connectionId, domain } = options 23 24 return { 25 id: "scalekit", 26 name: "Scalekit", 27 type: "oidc", 28 issuer, 29 authorization: { 30 params: { 31 scope: "openid email profile", 32 ...(connectionId && { connection_id: connectionId }), 33 ...(organizationId && { organization_id: organizationId }), 34 ...(domain && { domain }), 35 }, 36 }, 37 profile(profile) { 38 return { 39 id: profile.sub, 40 name: profile.name ?? `${profile.given_name} ${profile.family_name}`, 41 email: profile.email, 42 image: profile.picture ?? null, 43 } 44 }, 45 style: { bg: "#6f42c1", text: "#fff" }, 46 options, 47 } 48 } ``` After PR #13392 merges, replace the local import with: ```typescript 1 import Scalekit from "next-auth/providers/scalekit" ``` ### 4. Configure `auth.ts` [Section titled “4. Configure auth.ts”](#4-configure-authts) Create `auth.ts` in your project root: ```typescript 1 import NextAuth from "next-auth" 2 import Scalekit from "./providers/scalekit" // → "next-auth/providers/scalekit" after PR #13392 3 4 export const { handlers, auth, signIn, signOut } = NextAuth({ 5 providers: [ 6 Scalekit({ 7 issuer: process.env.AUTH_SCALEKIT_ISSUER!, 8 clientId: process.env.AUTH_SCALEKIT_ID!, 9 clientSecret: process.env.AUTH_SCALEKIT_SECRET!, 10 // Routing: set one of these (see step 7 for strategy) 11 connectionId: process.env.AUTH_SCALEKIT_CONNECTION_ID, 12 }), 13 ], 14 basePath: "/auth", 15 session: { strategy: "jwt" }, 16 }) ``` `basePath: "/auth"` is required to match the redirect URI you registered in step 1. Without it, Auth.js uses `/api/auth` and the Scalekit callback will fail. ### 5. Set environment variables [Section titled “5. Set environment variables”](#5-set-environment-variables) .env.local ```bash 1 # Generate with: npx auth secret 2 AUTH_SECRET= 3 4 # From Scalekit dashboard → API Keys 5 AUTH_SCALEKIT_ISSUER=https://yourenv.scalekit.dev 6 AUTH_SCALEKIT_ID=skc_... 7 AUTH_SCALEKIT_SECRET= 8 9 # Connection ID for development routing (conn_...) 10 # In production, resolve this dynamically per tenant — see step 7 11 AUTH_SCALEKIT_CONNECTION_ID=conn_... ``` `AUTH_SECRET` is not optional. Auth.js uses it to sign JWTs and encrypt session cookies. Missing it causes sign-in to fail silently. ### 6. Wire up route handlers [Section titled “6. Wire up route handlers”](#6-wire-up-route-handlers) Create `app/auth/[...nextauth]/route.ts`: ```typescript 1 import { handlers } from "@/auth" 2 export const { GET, POST } = handlers ``` This exposes `GET /auth/callback/scalekit` and `POST /auth/signout` — the endpoints Auth.js needs. The directory must be `app/auth/` (not `app/api/auth/`) to match the `basePath` you configured. ### 7. SSO routing strategies [Section titled “7. SSO routing strategies”](#7-sso-routing-strategies) Scalekit resolves which IdP connection to activate using these params (highest to lowest precedence): ```typescript 1 Scalekit({ 2 issuer: process.env.AUTH_SCALEKIT_ISSUER!, 3 clientId: process.env.AUTH_SCALEKIT_ID!, 4 clientSecret: process.env.AUTH_SCALEKIT_SECRET!, 5 6 // Option A — exact connection (dev / single-tenant use) 7 connectionId: "conn_...", 8 9 // Option B — org's active connection (multi-tenant: look up org from user's DB record) 10 organizationId: "org_...", 11 12 // Option C — resolve org from email domain (useful at login prompt) 13 domain: "acme.com", 14 }) ``` In production, don’t hardcode these values. Store `organizationId` or `connectionId` per tenant in your database, then construct the `signIn()` call dynamically based on the authenticated user’s org: ```typescript 1 // Example: look up org at sign-in time 2 const org = await db.organizations.findByDomain(emailDomain) 3 4 await signIn("scalekit", { 5 organizationId: org.scalekitOrgId, 6 redirectTo: "/dashboard", 7 }) ``` ### 8. Trigger sign-in and read the session [Section titled “8. Trigger sign-in and read the session”](#8-trigger-sign-in-and-read-the-session) A server component reads the session, and a sign-in form triggers the flow: app/page.tsx ```typescript 1 import { auth, signIn } from "@/auth" 2 3 export default async function Home() { 4 const session = await auth() 5 6 if (session) { 7 return ( 8

9

Signed in as {session.user?.email}

10
11 ) 12 } 13 14 return ( 15
{ 17 "use server" 18 await signIn("scalekit", { redirectTo: "/dashboard" }) 19 }} 20 > 21 22
23 ) 24 } ``` `session.user` includes `name`, `email`, and `image` normalized from the Scalekit OIDC profile. ## Testing [Section titled “Testing”](#testing) 1. Run `pnpm dev` and visit `http://localhost:3000`. 2. Click **Sign in with SSO** — you should be redirected to your IdP’s login page. 3. Complete authentication and confirm you land back on your app. 4. Check the session at `http://localhost:3000/api/auth/session` or read it from a server component — you should see `user.email` populated. If the redirect fails immediately, enable debug logging to trace the OIDC callback: ```bash 1 AUTH_DEBUG=true pnpm dev ``` ## Common mistakes [Section titled “Common mistakes”](#common-mistakes) 1. **Wrong redirect URI** — registering `/api/auth/callback/scalekit` instead of `/auth/callback/scalekit`. This guide sets `basePath: "/auth"` (a custom override, not the v5 default — the default remains `/api/auth`). The URI in Scalekit’s dashboard must match the callback path Auth.js actually uses. 2. **Missing `AUTH_SECRET`** — sign-in appears to start but fails on the callback with no visible error. Always set `AUTH_SECRET`. Generate one with `npx auth secret`. 3. **Hardcoding `connectionId` in production** — works in development, breaks for every other tenant. Store connection identifiers per-organization in your database and resolve them at runtime. 4. **Missing `basePath` in `auth.ts`** — if you omit `basePath: "/auth"`, Auth.js defaults to `/api/auth`. Your route handler must be at `app/api/auth/[...nextauth]/route.ts` and your redirect URI must use `/api/auth/callback/scalekit`. Pick one and be consistent. 5. **Using the wrong import path** — `next-auth/providers/scalekit` only resolves after PR #13392 merges. Until then, the local file at `./providers/scalekit` is the correct import. ## Production notes [Section titled “Production notes”](#production-notes) * **Rotate secrets without code changes** — update `AUTH_SCALEKIT_SECRET` in your environment configuration; Scalekit handles IdP certificate rotation automatically. * **Dynamic connection routing** — store `organizationId` or `connectionId` per tenant in your database. Resolve at sign-in time based on the user’s email domain or their existing tenant membership. * **Debug OIDC callback issues** — set `AUTH_DEBUG=true` temporarily in production to emit detailed callback traces. Remove it after diagnosing. * **Session persistence** — JWT sessions (the default) work without a database. If you need server-side session invalidation, add an Auth.js adapter (e.g. Prisma, Drizzle) and switch to `strategy: "database"`. * **Scalekit handles IdP complexity** — certificate rotation, SAML metadata updates, and attribute mapping changes happen in the Scalekit dashboard without touching your code. ## Next steps [Section titled “Next steps”](#next-steps) * [scalekit-developers/scalekit-authjs-example](https://github.com/scalekit-developers/scalekit-authjs-example) — full working repo for this cookbook * [Auth.js PR #13392](https://github.com/nextauthjs/next-auth/pull/13392) — track native Scalekit provider availability * [Scalekit SSO routing documentation](https://docs.scalekit.com/sso/quickstart) — full reference for `connection_id`, `organization_id`, and `domain` routing params * [Auth.js adapters](https://authjs.dev/getting-started/database) — add database-backed sessions for server-side invalidation * [Scalekit organization management API](https://docs.scalekit.com/apis) — look up `organizationId` dynamically from your tenant records --- # DOCUMENT BOUNDARY --- # Building a Custom Organization Switcher > Learn how to build your own organization switcher UI for complete control over multi-tenant user experiences. When users belong to multiple organizations, the default Scalekit organization switcher handles most use cases. However, some applications require deeper integration—a custom switcher embedded directly in your app’s navigation, or a specialized UI that matches your design system. This guide shows you how to build your own organization switcher using Scalekit’s APIs. ## Why build a custom switcher? [Section titled “Why build a custom switcher?”](#why-build-a-custom-switcher) The default Scalekit-hosted switcher works well for most scenarios. Build a custom switcher when you need: * **In-app navigation**: Users switch organizations without leaving your application * **Custom branding**: The switcher matches your application’s design language * **Specialized workflows**: Your app needs org-specific logic during switches * **Reduced redirects**: Avoid sending users through the authentication flow for every switch ## How the custom switcher works [Section titled “How the custom switcher works”](#how-the-custom-switcher-works) Your application handles the entire switching flow: 1. User authenticates through Scalekit and receives a session 2. Your app fetches the user’s organizations via the User Sessions API 3. You render your own organization selector UI 4. When a user selects an organization, your app updates the active context This approach gives you full control over the UI and routing, but requires you to manage session state and organization context within your application. ## Fetch user organizations [Section titled “Fetch user organizations”](#fetch-user-organizations) The User Sessions API returns the `authenticated_organizations` field containing all organizations the user can access. Use this data to populate your switcher UI. * Node.js Express.js ```javascript 1 // Use case: Get user's organizations for your switcher UI 2 // Security: Always validate session ownership before returning org data 3 const session = await scalekit.session.getSession(sessionId); 4 5 // Extract organizations from the session response 6 const organizations = session.authenticated_organizations || []; 7 8 // Render your organization switcher with this data 9 res.json({ organizations }); ``` * Python Flask ```python 1 # Use case: Get user's organizations for your switcher UI 2 # Security: Always validate session ownership before returning org data 3 session = scalekit_client.session.get_session(session_id) 4 5 # Extract organizations from the session response 6 organizations = session.get('authenticated_organizations', []) 7 8 # Render your organization switcher with this data 9 return jsonify({'organizations': organizations}) ``` * Go Gin ```go 1 // Use case: Get user's organizations for your switcher UI 2 // Security: Always validate session ownership before returning org data 3 session, err := scalekitClient.Session().GetSession(ctx, sessionId) 4 if err != nil { 5 return err 6 } 7 8 // Extract organizations from the session response 9 organizations := session.AuthenticatedOrganizations 10 11 // Render your organization switcher with this data 12 c.JSON(http.StatusOK, gin.H{"organizations": organizations}) ``` * Java Spring ```java 1 // Use case: Get user's organizations for your switcher UI 2 // Security: Always validate session ownership before returning org data 3 Session session = scalekitClient.sessions().getSession(sessionId); 4 5 // Extract organizations from the session response 6 List organizations = session.getAuthenticatedOrganizations(); 7 8 // Render your organization switcher with this data 9 return ResponseEntity.ok(Map.of("organizations", organizations)); ``` The response includes organization IDs, names, and metadata for each organization the user can access. ## Add domain context [Section titled “Add domain context”](#add-domain-context) Enhance your switcher by displaying which domains are associated with each organization. Use the Domains API to fetch this information. ```javascript 1 // Example: Fetch domains for an organization 2 const domains = await scalekit.domains.list({ organizationId: 'org_123' }); 3 4 // Display "@acme.com" next to the organization name in your UI ``` This helps users quickly identify the correct organization, especially when they belong to organizations with similar names. ## Handle organization selection [Section titled “Handle organization selection”](#handle-organization-selection) When a user selects an organization in your custom switcher, update your application’s context. Store the active organization ID in session storage or a cookie, then use it for subsequent API calls. * Node.js Express.js ```javascript 1 // Use case: Store selected organization and fetch org-specific data 2 app.post('/api/select-organization', async (req, res) => { 3 const { organizationId } = req.body; 4 const sessionId = req.session.scalekitSessionId; 5 6 // Security: Verify the user belongs to this organization 7 const session = await scalekit.session.getSession(sessionId); 8 const hasAccess = session.authenticated_organizations.some( 9 org => org.id === organizationId 10 ); 11 12 if (!hasAccess) { 13 return res.status(403).json({ error: 'Unauthorized' }); 14 } 15 16 // Store the active organization in the user's session 17 req.session.activeOrganizationId = organizationId; 18 19 res.json({ success: true }); 20 }); ``` * Python Flask ```python 1 # Use case: Store selected organization and fetch org-specific data 2 @app.route('/api/select-organization', methods=['POST']) 3 def select_organization(): 4 data = request.get_json() 5 organization_id = data.get('organizationId') 6 session_id = session.get('scalekit_session_id') 7 8 # Security: Verify the user belongs to this organization 9 user_session = scalekit_client.session.get_session(session_id) 10 has_access = any( 11 org['id'] == organization_id 12 for org in user_session.get('authenticated_organizations', []) 13 ) 14 15 if not has_access: 16 return jsonify({'error': 'Unauthorized'}), 403 17 18 # Store the active organization in the user's session 19 session['active_organization_id'] = organization_id 20 21 return jsonify({'success': True}) ``` * Go Gin ```go 1 // Use case: Store selected organization and fetch org-specific data 2 func SelectOrganization(c *gin.Context) { 3 var req struct { 4 OrganizationID string `json:"organizationId"` 5 } 6 if err := c.BindJSON(&req); err != nil { 7 c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"}) 8 return 9 } 10 11 sessionID := c.GetString("scalekitSessionID") 12 13 // Security: Verify the user belongs to this organization 14 session, err := scalekitClient.Session().GetSession(ctx, sessionID) 15 if err != nil { 16 c.JSON(http.StatusInternalServerError, gin.H{"error": "Session error"}) 17 return 18 } 19 20 hasAccess := false 21 for _, org := range session.AuthenticatedOrganizations { 22 if org.ID == req.OrganizationID { 23 hasAccess = true 24 break 25 } 26 } 27 28 if !hasAccess { 29 c.JSON(http.StatusForbidden, gin.H{"error": "Unauthorized"}) 30 return 31 } 32 33 // Store the active organization in the user's session 34 c.SetCookie("activeOrganizationID", req.OrganizationID, 3600, "/", "", true, true) 35 36 c.JSON(http.StatusOK, gin.H{"success": true}) 37 } ``` * Java Spring ```java 1 // Use case: Store selected organization and fetch org-specific data 2 @PostMapping("/api/select-organization") 3 public ResponseEntity selectOrganization( 4 @RequestBody Map request, 5 HttpSession httpSession 6 ) { 7 String organizationId = request.get("organizationId"); 8 String sessionId = (String) httpSession.getAttribute("scalekitSessionId"); 9 10 // Security: Verify the user belongs to this organization 11 Session session = scalekitClient.sessions().getSession(sessionId); 12 boolean hasAccess = session.getAuthenticatedOrganizations().stream() 13 .anyMatch(org -> org.getId().equals(organizationId)); 14 15 if (!hasAccess) { 16 return ResponseEntity.status(HttpStatus.FORBIDDEN) 17 .body(Map.of("error", "Unauthorized")); 18 } 19 20 // Store the active organization in the user's session 21 httpSession.setAttribute("activeOrganizationId", organizationId); 22 23 return ResponseEntity.ok(Map.of("success", true)); 24 } ``` Always verify that the user actually belongs to the organization they’re attempting to switch to. The `authenticated_organizations` array from the session is your source of truth for access control. ## When to use the hosted switcher instead [Section titled “When to use the hosted switcher instead”](#when-to-use-the-hosted-switcher-instead) The default Scalekit-hosted switcher is the right choice when: * You want the quickest implementation with minimal code * Your application doesn’t require in-app organization switching * You’re okay with users navigating through the authentication flow to switch organizations Build a custom switcher when user experience requirements demand deeper integration with your application’s UI and routing. You may refer to our [Sample Org Swithcer ](https://github.com/scalekit-inc/Nextjs-Django-Org-Switcher-Example/tree/main)application to better understand how the API calls enable this custom org switcher that is embedded inside your application. --- # DOCUMENT BOUNDARY --- # Build a daily briefing agent with Vercel AI SDK and Scalekit Agent Auth > Connect a TypeScript or Python agent via Vercel AI SDK and Scalekit Agent Auth to Google Calendar and Gmail using two integration patterns. A daily briefing agent needs two things: today’s calendar events and the latest unread emails. Both live behind OAuth-protected APIs, and each requires its own token, its own authorization flow, and its own refresh logic. Before you write any scheduling logic, you’re already maintaining two parallel token lifecycles. Scalekit eliminates that overhead. It stores one OAuth session per connector per user, handles token refresh automatically, and gives you a single API surface regardless of which provider you’re talking to. This recipe shows how to use it with Google Calendar and Gmail — and demonstrates two patterns for consuming those credentials in your agent. **What this recipe covers:** * **OAuth token pattern** — Scalekit provides a valid token; your agent calls the Google Calendar REST API directly. Use this when you need full control over the request. * **Built-in action pattern** — Your agent calls `execute_tool("gmail_fetch_mails")`; Scalekit executes the Gmail API call and returns structured data. Use this when you want speed and don’t need to customize the request. The complete source used here is available in the [vercel-ai-agent-toolkit](https://github.com/scalekit-developers/vercel-ai-agent-toolkit) repository, with a TypeScript implementation using the Vercel AI SDK and a Python implementation using the Anthropic SDK directly. ### 1. Set up connections in Scalekit [Section titled “1. Set up connections in Scalekit”](#1-set-up-connections-in-scalekit) In the [Scalekit Dashboard](https://app.scalekit.com), create two connections under **AgentKit** > **Connections** > **Create Connection**: * `googlecalendar` — Google Calendar OAuth connection * `gmail` — Gmail OAuth connection The connection names are identifiers your code references directly. They must match exactly. ### 2. Install dependencies [Section titled “2. Install dependencies”](#2-install-dependencies) * TypeScript ```bash 1 cd typescript 2 pnpm install ``` The `typescript/package.json` includes: ```json 1 { 2 "dependencies": { 3 "ai": "^4.3.15", 4 "@ai-sdk/anthropic": "^1.2.12", 5 "@scalekit-sdk/node": "2.2.0-beta.1", 6 "zod": "^3.0.0", 7 "dotenv": "^16.0.0" 8 } 9 } ``` * Python ```bash 1 cd python 2 uv venv .venv 3 uv pip install -r requirements.txt ``` The `python/requirements.txt` includes: ```text 1 scalekit-sdk-python 2 anthropic 3 requests 4 python-dotenv ``` ### 3. Configure credentials [Section titled “3. Configure credentials”](#3-configure-credentials) Copy the example env file and fill in your credentials: ```bash 1 cp typescript/.env.example typescript/.env # TypeScript 2 cp typescript/.env.example python/.env # Python (same variables) ``` .env ```bash 1 SCALEKIT_ENV_URL=https://your-env.scalekit.dev 2 SCALEKIT_CLIENT_ID=skc_... 3 SCALEKIT_CLIENT_SECRET=your-secret 4 5 ANTHROPIC_API_KEY=sk-ant-... ``` Get your Scalekit credentials at **app.scalekit.com → Settings → API Credentials**. ### 4. Initialize the Scalekit client [Section titled “4. Initialize the Scalekit client”](#4-initialize-the-scalekit-client) * TypeScript ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node'; 2 import { ConnectorStatus } from '@scalekit-sdk/node/lib/pkg/grpc/scalekit/v1/connected_accounts/connected_accounts_pb.js'; 3 import 'dotenv/config'; 4 5 // Never hard-code credentials — they would be exposed in source control. 6 // Pull them from environment variables at runtime. 7 const scalekit = new ScalekitClient( 8 process.env.SCALEKIT_ENV_URL!, 9 process.env.SCALEKIT_CLIENT_ID!, 10 process.env.SCALEKIT_CLIENT_SECRET!, 11 ); 12 13 const USER_ID = 'user_123'; // Replace with the real user ID from your session ``` `ConnectorStatus` is imported from the SDK’s generated protobuf file. Compare `connectedAccount.status` against `ConnectorStatus.ACTIVE` rather than the string `'ACTIVE'` — TypeScript’s type system enforces this. * Python ```python 1 import os 2 import json 3 import requests 4 from datetime import datetime, timezone 5 from dotenv import load_dotenv 6 import anthropic 7 import scalekit.client 8 9 load_dotenv() 10 11 # Never hard-code credentials — they would be exposed in source control. 12 # Pull them from environment variables at runtime. 13 scalekit_client = scalekit.client.ScalekitClient( 14 client_id=os.environ["SCALEKIT_CLIENT_ID"], 15 client_secret=os.environ["SCALEKIT_CLIENT_SECRET"], 16 env_url=os.environ["SCALEKIT_ENV_URL"], 17 ) 18 actions = scalekit_client.actions 19 20 USER_ID = "user_123" # Replace with the real user ID from your session ``` `scalekit_client.actions` is the entry point for all connected-account operations: creating accounts, generating auth links, fetching tokens, and executing built-in tools. ### 5. Ensure each connector is authorized [Section titled “5. Ensure each connector is authorized”](#5-ensure-each-connector-is-authorized) Before calling any API, check whether the user has an active connected account. If not, print an authorization link and wait for them to complete the browser OAuth flow. * TypeScript ```typescript 1 async function ensureConnected(connector: string) { 2 const { connectedAccount } = 3 await scalekit.connectedAccounts.getOrCreateConnectedAccount({ 4 connector, 5 identifier: USER_ID, 6 }); 7 8 if (connectedAccount?.status !== ConnectorStatus.ACTIVE) { 9 const { link } = 10 await scalekit.connectedAccounts.getMagicLinkForConnectedAccount({ 11 connector, 12 identifier: USER_ID, 13 }); 14 console.log(`\n[${connector}] Authorization required.`); 15 console.log(`Open this link:\n\n ${link}\n`); 16 console.log('Press Enter once you have completed the OAuth flow...'); 17 await new Promise(resolve => { 18 process.stdin.resume(); 19 process.stdin.once('data', () => { process.stdin.pause(); resolve(); }); 20 }); 21 } 22 23 return connectedAccount; 24 } ``` * Python ```python 1 def ensure_connected(connector: str): 2 response = actions.get_or_create_connected_account( 3 connection_name=connector, 4 identifier=USER_ID, 5 ) 6 connected_account = response.connected_account 7 8 if connected_account.status != "ACTIVE": 9 link_response = actions.get_authorization_link( 10 connection_name=connector, 11 identifier=USER_ID, 12 ) 13 print(f"\n[{connector}] Authorization required.") 14 print(f"Open this link:\n\n {link_response.link}\n") 15 input("Press Enter once you have completed the OAuth flow...") 16 17 return connected_account ``` After the first successful authorization, `getOrCreateConnectedAccount` / `get_or_create_connected_account` returns an active account on all subsequent calls. Scalekit refreshes expired tokens automatically — your code never calls a token-refresh endpoint. ### 6. Fetch calendar events using the OAuth token pattern [Section titled “6. Fetch calendar events using the OAuth token pattern”](#6-fetch-calendar-events-using-the-oauth-token-pattern) For Google Calendar, retrieve a valid access token from Scalekit and call the Google Calendar REST API directly. This pattern gives you full control over query parameters, pagination, and error handling. * TypeScript ```typescript 1 async function getAccessToken(connector: string): Promise { 2 const response = 3 await scalekit.connectedAccounts.getConnectedAccountByIdentifier({ 4 connector, 5 identifier: USER_ID, 6 }); 7 const details = response?.connectedAccount?.authorizationDetails?.details; 8 if (details?.case === 'oauthToken' && details.value?.accessToken) { 9 return details.value.accessToken; 10 } 11 throw new Error(`No access token found for ${connector}`); 12 } ``` Use this token in a tool that the LLM can call: ```typescript 1 import { tool } from 'ai'; 2 import { z } from 'zod'; 3 4 const today = new Date(); 5 const timeMin = new Date(today.getFullYear(), today.getMonth(), today.getDate()).toISOString(); 6 const timeMax = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 23, 59, 59).toISOString(); 7 8 const calendarToken = await getAccessToken('googlecalendar'); 9 10 const getCalendarEvents = tool({ 11 description: "Fetch today's events from Google Calendar", 12 parameters: z.object({ 13 maxResults: z.number().optional().default(5), 14 }), 15 execute: async ({ maxResults }) => { 16 const url = new URL('https://www.googleapis.com/calendar/v3/calendars/primary/events'); 17 url.searchParams.set('timeMin', timeMin); 18 url.searchParams.set('timeMax', timeMax); 19 url.searchParams.set('maxResults', String(maxResults)); 20 url.searchParams.set('orderBy', 'startTime'); 21 url.searchParams.set('singleEvents', 'true'); 22 23 const res = await fetch(url.toString(), { 24 headers: { Authorization: `Bearer ${calendarToken}` }, 25 }); 26 if (!res.ok) throw new Error(`Calendar API error: ${res.status}`); 27 const data = await res.json() as { items?: unknown[] }; 28 return data.items ?? []; 29 }, 30 }); ``` * Python ```python 1 def get_access_token(connector: str) -> str: 2 # Use get_or_create_connected_account as the safe default so 3 # first-time users do not hit RESOURCE_NOT_FOUND. 4 response = actions.get_or_create_connected_account( 5 connection_name=connector, 6 identifier=USER_ID, 7 ) 8 connected_account = response.connected_account 9 if connected_account.status != "ACTIVE": 10 raise RuntimeError( 11 f"{connector} is not active yet. Complete authorization first." 12 ) 13 14 tokens = connected_account.authorization_details["oauth_token"] 15 return tokens["access_token"] 16 17 def fetch_calendar_events(access_token: str, max_results: int = 5) -> list: 18 today = datetime.now(timezone.utc).astimezone() 19 time_min = today.replace(hour=0, minute=0, second=0, microsecond=0).isoformat() 20 time_max = today.replace(hour=23, minute=59, second=59, microsecond=0).isoformat() 21 22 resp = requests.get( 23 "https://www.googleapis.com/calendar/v3/calendars/primary/events", 24 headers={"Authorization": f"Bearer {access_token}"}, 25 params={ 26 "timeMin": time_min, 27 "timeMax": time_max, 28 "maxResults": max_results, 29 "orderBy": "startTime", 30 "singleEvents": "true", 31 }, 32 ) 33 resp.raise_for_status() 34 return resp.json().get("items", []) ``` ### 7. Fetch emails using the built-in action pattern [Section titled “7. Fetch emails using the built-in action pattern”](#7-fetch-emails-using-the-built-in-action-pattern) For Gmail, call `execute_tool` with the built-in `gmail_fetch_mails` action. Scalekit executes the Gmail API call using the stored token and returns structured data. You don’t need to build the request, handle the token, or parse the response format. * TypeScript ```typescript 1 const getUnreadEmails = tool({ 2 description: 'Fetch top unread emails from Gmail via Scalekit actions', 3 parameters: z.object({ 4 maxResults: z.number().optional().default(5), 5 }), 6 execute: async ({ maxResults }) => { 7 const response = await scalekit.tools.executeTool({ 8 toolName: 'gmail_fetch_mails', 9 connectedAccountId: gmailAccount?.id, 10 params: { 11 query: 'is:unread', 12 max_results: maxResults, 13 }, 14 }); 15 return response.data?.toJson() ?? {}; 16 }, 17 }); ``` * Python ```python 1 def fetch_unread_emails(connected_account_id: str, max_results: int = 5) -> dict: 2 response = actions.execute_tool( 3 tool_name="gmail_fetch_mails", 4 connected_account_id=connected_account_id, 5 tool_input={ 6 "query": "is:unread", 7 "max_results": max_results, 8 }, 9 ) 10 return response.result ``` The built-in action pattern trades flexibility for brevity. You can’t customize headers or pagination, but you also don’t need to read Gmail API documentation — the tool parameters are consistent across all Scalekit connectors. See [all supported agent connectors](/agentkit/connectors/) for the full list of built-in tools. ### 8. Wire the agent together [Section titled “8. Wire the agent together”](#8-wire-the-agent-together) Pass both tools to the LLM and ask for a daily summary. * TypeScript The TypeScript version uses the Vercel AI SDK’s `generateText` with `maxSteps` to allow the LLM to call multiple tools in sequence before producing the final response. ```typescript 1 import { generateText } from 'ai'; 2 import { anthropic } from '@ai-sdk/anthropic'; 3 4 const [calendarAccount, gmailAccount] = await Promise.all([ 5 ensureConnected('googlecalendar'), 6 ensureConnected('gmail'), 7 ]); 8 9 const calendarToken = await getAccessToken('googlecalendar'); 10 11 const { text } = await generateText({ 12 model: anthropic('claude-sonnet-4-6'), 13 prompt: `Give me a summary of my day for ${today.toDateString()}: list today's calendar events and my top 5 unread emails.`, 14 tools: { 15 getCalendarEvents, 16 getUnreadEmails, 17 }, 18 maxSteps: 5, // allow the LLM to call multiple tools before responding 19 }); 20 21 console.log(text); ``` `maxSteps` controls how many tool-call rounds the LLM can make before it must return a final text response. Without it, `generateText` stops after the first tool call. * Python The Python version uses the Anthropic SDK directly with a manual agentic loop. The loop continues until the model returns `stop_reason == "end_turn"` with no pending tool calls. ```python 1 def run_agent(): 2 gmail_account = ensure_connected("gmail") 3 ensure_connected("googlecalendar") 4 calendar_token = get_access_token("googlecalendar") 5 6 client = anthropic.Anthropic() 7 today = datetime.now().strftime("%A, %B %d, %Y") 8 9 tools = [ 10 { 11 "name": "get_calendar_events", 12 "description": "Fetch today's events from Google Calendar", 13 "input_schema": { 14 "type": "object", 15 "properties": {"max_results": {"type": "integer", "default": 5}}, 16 }, 17 }, 18 { 19 "name": "get_unread_emails", 20 "description": "Fetch top unread emails from Gmail via Scalekit actions", 21 "input_schema": { 22 "type": "object", 23 "properties": {"max_results": {"type": "integer", "default": 5}}, 24 }, 25 }, 26 ] 27 28 messages = [ 29 { 30 "role": "user", 31 "content": f"Give me a summary of my day for {today}: list today's calendar events and my top 5 unread emails.", 32 } 33 ] 34 35 while True: 36 response = client.messages.create( 37 model="claude-sonnet-4-6", 38 max_tokens=1024, 39 tools=tools, 40 messages=messages, 41 ) 42 messages.append({"role": "assistant", "content": response.content}) 43 44 if response.stop_reason == "end_turn": 45 for block in response.content: 46 if hasattr(block, "text"): 47 print(block.text) 48 break 49 50 tool_results = [] 51 for block in response.content: 52 if block.type == "tool_use": 53 max_results = block.input.get("max_results", 5) 54 if block.name == "get_calendar_events": 55 result = fetch_calendar_events(calendar_token, max_results) 56 elif block.name == "get_unread_emails": 57 result = fetch_unread_emails(gmail_account.id, max_results) 58 else: 59 result = {"error": f"Unknown tool: {block.name}"} 60 tool_results.append({ 61 "type": "tool_result", 62 "tool_use_id": block.id, 63 "content": json.dumps(result), 64 }) 65 66 if tool_results: 67 messages.append({"role": "user", "content": tool_results}) 68 else: 69 break 70 71 if __name__ == "__main__": 72 run_agent() ``` ### 9. Testing [Section titled “9. Testing”](#9-testing) Run the agent: * TypeScript ```bash 1 cd typescript && pnpm start ``` * Python ```bash 1 cd python && .venv/bin/python index.py ``` On first run, you see two authorization prompts in sequence: ```text 1 [googlecalendar] Authorization required. 2 Open this link: 3 4 https://auth.scalekit.dev/connect/... 5 6 Press Enter once you have completed the OAuth flow... 7 8 [gmail] Authorization required. 9 Open this link: 10 11 https://auth.scalekit.dev/connect/... 12 13 Press Enter once you have completed the OAuth flow... ``` After both connectors are authorized, the agent fetches your data and returns a summary: ```text 1 Here's your day for Friday, March 27, 2026: 2 3 📅 Calendar — 3 events today 4 • 9:00 AM Team standup (30 min) 5 • 1:00 PM Product review 6 • 4:00 PM 1:1 with manager 7 8 📧 Unread emails — top 5 9 • "Q1 roadmap feedback needed" — Sarah Chen, 1h ago 10 • "Deploy failed: production" — GitHub Actions, 2h ago 11 • "New PR review requested" — Lin Feng, 3h ago 12 ... ``` On subsequent runs, both authorization prompts are skipped. Scalekit returns the active session directly. ## Common mistakes [Section titled “Common mistakes”](#common-mistakes) ## Production notes [Section titled “Production notes”](#production-notes) **User ID from session** — Both implementations hardcode `USER_ID = "user_123"`. In production, replace this with the real user identifier from your application’s session. A mismatch means Scalekit looks up the wrong user’s tokens. **Token freshness** — `getConnectedAccountByIdentifier` (TypeScript) and `get_connected_account` (Python) always return a fresh token — Scalekit refreshes it before returning if it has expired. You do not need to track expiry or call a refresh endpoint. **First-run blocking** — The authorization prompt blocks the process until the user completes OAuth in the browser. In a web application, redirect the user to `link` instead of printing it, and handle the callback before proceeding. **`execute_tool` response shape** — In Python, `response.result` is a dictionary whose structure depends on the tool. In TypeScript, `response.data?.toJson()` converts the protobuf response to a plain object. Log the raw response on first use to understand the shape before passing it to the LLM. **Rate limits** — The Google Calendar API and Gmail API both have per-user daily quotas. If your agent runs frequently, add exponential backoff around the API calls and cache calendar events across requests where freshness allows. ## Next steps [Section titled “Next steps”](#next-steps) * **Add more connectors** — The same `ensureConnected` pattern works for any Scalekit-supported connector. Swap the connector name and replace the Google API calls with the target service’s API. See [all supported connectors](/agentkit/connectors/). * **Use the built-in Calendar action** — Scalekit also provides a `googlecalendar_list_events` built-in action. If you don’t need custom query parameters, switch the Calendar tool to `execute_tool` and remove the `getAccessToken` call entirely. * **Stream the response** — Replace `generateText` with `streamText` in the Vercel AI SDK to stream the LLM’s summary token-by-token instead of waiting for the full response. * **Handle re-authorization** — If a user revokes access, `getOrCreateConnectedAccount` returns an inactive account. Add a re-authorization path to recover gracefully instead of crashing. * **Review the agent auth quickstart** — For a broader overview of the connected-accounts model and supported providers, see the [agent auth quickstart](/agentkit/quickstart/). --- # DOCUMENT BOUNDARY --- # Implementing Passwordless Auth in Next.js 15 > Add magic link and OTP authentication to your Next.js application using Scalekit's headless API. Next.js 15’s App Router expects authentication to be server-first: tokens generated on the server, verification happening in Route Handlers or Server Actions, and sessions stored in HttpOnly cookies. If you’re building passwordless authentication (magic links + OTP), traditional client-side SDKs won’t work properly with this model. This cookbook shows you how to implement passwordless auth that works natively with Next.js 15’s architecture using Scalekit’s headless API. ## The problem [Section titled “The problem”](#the-problem) You want passwordless authentication in Next.js 15 but face these challenges: * **Client-side SDKs break App Router patterns** - They expect browser-side token handling, which violates server-first principles * **Vendor UIs don’t match your design** - Pre-built login pages force you to compromise on branding * **DIY is complex** - Building secure token generation, email delivery, verification, and session management from scratch is a significant lift * **Cross-device failures** - Magic links often break when users switch devices or email clients strip parameters ## Who needs this [Section titled “Who needs this”](#who-needs-this) This cookbook is for you if: * ✅ You’re building a Next.js 15 application using App Router * ✅ You want passwordless authentication (magic links, OTP, or both) * ✅ You need full control over your login UI and email design * ✅ You don’t want to migrate your existing user database * ✅ You require server-side security for compliance You **don’t** need this if: * ❌ You’re happy with vendor-hosted login pages * ❌ You’re using Next.js Pages Router (not App Router) * ❌ You prefer traditional username/password authentication ## The solution [Section titled “The solution”](#the-solution) Scalekit’s passwordless API provides three server-side methods that integrate directly with Next.js 15’s architecture: 1. **`sendPasswordlessEmail()`** - Generates and sends magic link/OTP to user’s email 2. **`verifyPasswordlessEmail()`** - Validates the token/code and returns verified identity 3. **`resendPasswordlessEmail()`** - Issues a fresh credential if the first expires All security logic stays server-side, works with Server Actions and Route Handlers, and integrates with Edge Middleware for route protection. ## Implementation [Section titled “Implementation”](#implementation) ### 1. Configure Scalekit dashboard [Section titled “1. Configure Scalekit dashboard”](#1-configure-scalekit-dashboard) Enable passwordless authentication in your [Scalekit dashboard](https://app.scalekit.com/): 1. Navigate to **Authentication → Passwordless** 2. Select **Magic Link + Verification Code** for maximum reliability 3. Set **Expiry Period** (e.g., 600 seconds for 10-minute lifetime) 4. Enable **Enforce same browser origin** to prevent link hijacking 5. (Optional) Enable **Regenerate credentials on resend** to invalidate old links ### 2. Install dependencies and configure environment [Section titled “2. Install dependencies and configure environment”](#2-install-dependencies-and-configure-environment) ```bash 1 npm install @scalekit-sdk/node jsonwebtoken ``` Create `.env.local`: ```bash 1 SCALEKIT_ENVIRONMENT_URL=env_xxxx 2 SCALEKIT_CLIENT_ID=skc_xxx 3 SCALEKIT_CLIENT_SECRET=your_secret 4 APP_URL=http://localhost:3000 5 JWT_SECRET=your_jwt_secret ``` ### 3. Create session management utilities [Section titled “3. Create session management utilities”](#3-create-session-management-utilities) Create `lib/session-store.ts` to handle server-side session creation: ```typescript 1 import jwt from 'jsonwebtoken'; 2 import { cookies } from 'next/headers'; 3 4 const COOKIE = 'session'; 5 const SECRET = process.env.JWT_SECRET!; 6 7 export function createSession(email: string) { 8 const token = jwt.sign({ email }, SECRET, { expiresIn: '7d' }); 9 cookies().set(COOKIE, token, { 10 httpOnly: true, 11 secure: process.env.NODE_ENV === 'production', 12 sameSite: 'lax', 13 path: '/', 14 maxAge: 60 * 60 * 24 * 7, 15 }); 16 } 17 18 export function readSessionEmail(): string | null { 19 const token = cookies().get(COOKIE)?.value; 20 if (!token) return null; 21 22 try { 23 const decoded = jwt.verify(token, SECRET) as { email: string }; 24 return decoded.email; 25 } catch { 26 return null; 27 } 28 } 29 30 export function clearSession() { 31 cookies().delete(COOKIE); 32 } ``` ### 4. Create send email endpoint [Section titled “4. Create send email endpoint”](#4-create-send-email-endpoint) Create `app/api/auth/send-passwordless/route.ts`: ```typescript 1 import Scalekit from '@scalekit-sdk/node'; 2 import { NextRequest, NextResponse } from 'next/server'; 3 4 const scalekit = new Scalekit( 5 process.env.SCALEKIT_ENVIRONMENT_URL!, 6 process.env.SCALEKIT_CLIENT_ID!, 7 process.env.SCALEKIT_CLIENT_SECRET! 8 ); 9 10 export async function POST(req: NextRequest) { 11 const { email } = await req.json(); 12 13 try { 14 const response = await scalekit.passwordless.sendPasswordlessEmail(email, { 15 template: 'SIGNIN', 16 expiresIn: 600, // 10 minutes 17 state: crypto.randomUUID(), 18 magiclinkAuthUri: `${process.env.APP_URL}/api/auth/verify`, 19 }); 20 21 return NextResponse.json({ 22 authRequestId: response.authRequestId, 23 expiresAt: response.expiresAt, 24 }); 25 } catch (error) { 26 return NextResponse.json( 27 { error: 'Failed to send email' }, 28 { status: 500 } 29 ); 30 } 31 } ``` ### 5. Create verification endpoint [Section titled “5. Create verification endpoint”](#5-create-verification-endpoint) Create `app/api/auth/verify/route.ts` with both GET (magic link) and POST (OTP) handlers: ```typescript 1 import Scalekit from '@scalekit-sdk/node'; 2 import { NextRequest, NextResponse } from 'next/server'; 3 import { createSession } from '@/lib/session-store'; 4 5 const scalekit = new Scalekit( 6 process.env.SCALEKIT_ENVIRONMENT_URL!, 7 process.env.SCALEKIT_CLIENT_ID!, 8 process.env.SCALEKIT_CLIENT_SECRET! 9 ); 10 11 // Magic link verification 12 export async function GET(req: NextRequest) { 13 const url = new URL(req.url); 14 const linkToken = url.searchParams.get('link_token'); 15 const authRequestId = url.searchParams.get('auth_request_id') ?? undefined; 16 17 if (!linkToken) { 18 return NextResponse.redirect( 19 new URL('/login?error=missing_token', req.url) 20 ); 21 } 22 23 try { 24 const verified = await scalekit.passwordless.verifyPasswordlessEmail( 25 { linkToken }, 26 authRequestId 27 ); 28 29 createSession(verified.email); 30 return NextResponse.redirect(new URL('/dashboard', req.url)); 31 } catch { 32 return NextResponse.redirect( 33 new URL('/login?error=verification_failed', req.url) 34 ); 35 } 36 } 37 38 // OTP verification 39 export async function POST(req: NextRequest) { 40 const { code, authRequestId } = await req.json(); 41 42 if (!code || !authRequestId) { 43 return NextResponse.json( 44 { error: 'Missing required fields' }, 45 { status: 400 } 46 ); 47 } 48 49 try { 50 const verified = await scalekit.passwordless.verifyPasswordlessEmail( 51 { code }, 52 authRequestId 53 ); 54 55 createSession(verified.email); 56 return NextResponse.json({ success: true }); 57 } catch { 58 return NextResponse.json( 59 { error: 'Invalid or expired code' }, 60 { status: 400 } 61 ); 62 } 63 } ``` ### 6. Add resend endpoint [Section titled “6. Add resend endpoint”](#6-add-resend-endpoint) Create `app/api/auth/resend-passwordless/route.ts`: ```typescript 1 import Scalekit from '@scalekit-sdk/node'; 2 import { NextRequest, NextResponse } from 'next/server'; 3 4 const scalekit = new Scalekit( 5 process.env.SCALEKIT_ENVIRONMENT_URL!, 6 process.env.SCALEKIT_CLIENT_ID!, 7 process.env.SCALEKIT_CLIENT_SECRET! 8 ); 9 10 export async function POST(req: NextRequest) { 11 const { authRequestId } = await req.json(); 12 13 if (!authRequestId) { 14 return NextResponse.json( 15 { error: 'Missing authRequestId' }, 16 { status: 400 } 17 ); 18 } 19 20 try { 21 const response = await scalekit.passwordless.resendPasswordlessEmail( 22 authRequestId 23 ); 24 25 return NextResponse.json({ 26 authRequestId: response.authRequestId, 27 expiresAt: response.expiresAt, 28 }); 29 } catch { 30 return NextResponse.json( 31 { error: 'Resend failed' }, 32 { status: 400 } 33 ); 34 } 35 } ``` ### 7. Protect routes with middleware [Section titled “7. Protect routes with middleware”](#7-protect-routes-with-middleware) Create `middleware.ts` in your project root: ```typescript 1 import { NextRequest, NextResponse } from 'next/server'; 2 3 export function middleware(req: NextRequest) { 4 const protectedPath = req.nextUrl.pathname.startsWith('/dashboard'); 5 const hasSession = Boolean(req.cookies.get('session')?.value); 6 7 if (protectedPath && !hasSession) { 8 const url = new URL('/login', req.url); 9 url.searchParams.set('next', req.nextUrl.pathname); 10 return NextResponse.redirect(url); 11 } 12 13 return NextResponse.next(); 14 } 15 16 export const config = { 17 matcher: ['/dashboard/:path*'], 18 }; ``` ### 8. Build login UI (example) [Section titled “8. Build login UI (example)”](#8-build-login-ui-example) Create `app/login/page.tsx`: ```typescript 1 'use client'; 2 3 import { useState } from 'react'; 4 import { useRouter } from 'next/navigation'; 5 6 export default function LoginPage() { 7 const [email, setEmail] = useState(''); 8 const [authRequestId, setAuthRequestId] = useState(''); 9 const [showOtp, setShowOtp] = useState(false); 10 const [otp, setOtp] = useState(''); 11 const router = useRouter(); 12 13 async function handleSendEmail(e: React.FormEvent) { 14 e.preventDefault(); 15 16 const res = await fetch('/api/auth/send-passwordless', { 17 method: 'POST', 18 headers: { 'Content-Type': 'application/json' }, 19 body: JSON.stringify({ email }), 20 }); 21 22 const data = await res.json(); 23 setAuthRequestId(data.authRequestId); 24 setShowOtp(true); 25 } 26 27 async function handleVerifyOtp(e: React.FormEvent) { 28 e.preventDefault(); 29 30 const res = await fetch('/api/auth/verify', { 31 method: 'POST', 32 headers: { 'Content-Type': 'application/json' }, 33 body: JSON.stringify({ code: otp, authRequestId }), 34 }); 35 36 if (res.ok) { 37 router.push('/dashboard'); 38 } 39 } 40 41 return ( 42
43 {!showOtp ? ( 44
45 setEmail(e.target.value)} 49 placeholder="Enter your email" 50 required 51 /> 52 53
54 ) : ( 55
56

Check your email for a magic link or enter the code below:

57 setOtp(e.target.value)} 61 placeholder="Enter 6-digit code" 62 maxLength={6} 63 /> 64 65
66 )} 67
68 ); 69 } ``` ## Security features [Section titled “Security features”](#security-features) Scalekit enforces these protections automatically: * **Rate limiting**: 2 emails per minute per address, 5 OTP attempts per 10 minutes * **Short-lived tokens**: Configure expiry from 60 seconds to 1 hour * **Same-browser enforcement**: When enabled, links can only be verified from the originating browser * **HttpOnly sessions**: Tokens never touch client JavaScript ## Error handling [Section titled “Error handling”](#error-handling) Map Scalekit errors to user-friendly messages: ```typescript 1 function getErrorMessage(error: string): string { 2 if (error.includes('expired')) { 3 return 'This link has expired. Request a new one.'; 4 } 5 if (error.includes('rate')) { 6 return 'Too many attempts. Please try again later.'; 7 } 8 if (error.includes('invalid')) { 9 return 'Invalid code. Please check and try again.'; 10 } 11 return 'Verification failed. Please try again.'; 12 } ``` ## Production checklist [Section titled “Production checklist”](#production-checklist) Before deploying: * ✅ Set `secure: true` for session cookies (enforced automatically in production) * ✅ Configure production Scalekit credentials in environment variables * ✅ Verify dashboard settings match your security requirements * ✅ Test magic link + OTP flow on multiple email clients * ✅ Set up monitoring for authentication errors and rate limit hits * ✅ Configure custom email templates with your branding ## Complete example [Section titled “Complete example”](#complete-example) Full working code is available in the [Scalekit GitHub repository](https://github.com/scalekit-developers/blogops-app-examples/tree/main/nextjs-passwordless-auth). ## Why this approach works [Section titled “Why this approach works”](#why-this-approach-works) This implementation: * **Works natively with App Router** - All sensitive operations are server-side * **Maintains full UI control** - No vendor widgets or redirects to hosted pages * **Handles cross-device gracefully** - OTP fallback covers magic link failures * **Requires no user migration** - Works on top of your existing user store * **Stays secure by default** - HttpOnly cookies, server-only verification, automatic rate limiting ## Related resources [Section titled “Related resources”](#related-resources) * [Scalekit Passwordless Auth Documentation](https://docs.scalekit.com/passwordless/) * [Next.js 15 App Router Documentation](https://nextjs.org/docs/app) * [Full tutorial blog post](https://www.scalekit.com/blog/passwordless-authentication-next-js) --- # DOCUMENT BOUNDARY --- # Configuring JWT Validation Timeouts in Spring Boot 4.0+ > Fix connection timeout errors when validating Scalekit JWT tokens in Spring Boot 4.0.0 and later versions. If you’re using Spring Boot 4.0.0 or later and experiencing connection timeout errors when validating JWT tokens from Scalekit, you’ll need to explicitly configure timeout values. This is a known issue affecting Spring Security’s OAuth2 resource server configuration. ## The problem [Section titled “The problem”](#the-problem) Your Spring Boot application successfully configures the `issuer-uri` for JWT validation: ```yaml 1 spring: 2 security: 3 oauth2: 4 resourceserver: 5 jwt: 6 issuer-uri: https://auth.scalekit.com ``` But authentication fails with timeout errors like: ```plaintext 1 java.net.SocketTimeoutException: Connect timed out 2 at org.springframework.security.oauth2.jwt.JwtDecoders.fromIssuerLocation ``` ## Why this happens [Section titled “Why this happens”](#why-this-happens) Starting with Spring Boot 4.0.0, Spring Security changed how it handles HTTP connections during JWT validation: * **Before 4.0.0**: Spring used default system timeouts (often much longer) * **After 4.0.0**: Spring enforces strict, short timeout defaults that can be too aggressive for production When your application starts or validates its first JWT token, Spring Security: 1. Fetches the OpenID Connect discovery document from `issuer-uri` 2. Retrieves the JWKS (JSON Web Key Set) to verify token signatures 3. Caches these for future validations If these initial requests timeout, authentication fails completely. ## Who needs this fix [Section titled “Who needs this fix”](#who-needs-this-fix) This issue specifically affects: * ✅ Spring Boot applications version **4.0.0 or later** * ✅ Using `issuer-uri` for JWT validation (not manual `jwk-set-uri`) * ✅ Production environments with network latency or firewall rules * ✅ Applications experiencing intermittent authentication failures You **don’t** need this if: * ❌ Using Spring Boot 3.x or earlier * ❌ Manually configuring `jwk-set-uri` instead of `issuer-uri` * ❌ Already have custom `RestTemplate` or `WebClient` configurations ## The solution [Section titled “The solution”](#the-solution) For Spring Security servlet resource servers, there are no properties to configure JWT discovery/JWKS HTTP timeouts. Use a custom `JwtDecoder` bean with `RestOperations` (for example, `RestTemplate`) and explicit timeout values: ```java 1 import org.springframework.context.annotation.Bean; 2 import org.springframework.context.annotation.Configuration; 3 import org.springframework.http.client.SimpleClientHttpRequestFactory; 4 import org.springframework.security.oauth2.jwt.JwtDecoder; 5 import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; 6 import org.springframework.web.client.RestTemplate; 7 8 @Configuration 9 public class SecurityConfig { 10 11 @Bean 12 public JwtDecoder jwtDecoder() { 13 // Create a RestTemplate with custom timeouts 14 SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); 15 factory.setConnectTimeout(10000); // 10 seconds 16 factory.setReadTimeout(10000); // 10 seconds 17 18 RestTemplate restTemplate = new RestTemplate(factory); 19 20 // Use the custom RestTemplate for JWT validation 21 return NimbusJwtDecoder 22 .withIssuerLocation("https://auth.scalekit.com") 23 .restOperations(restTemplate) 24 .build(); 25 } 26 } ``` This gives you: * Full control over HTTP client configuration * Ability to add custom headers or interceptors * Environment-specific timeout tuning (development: 5000ms, production: 10000–15000ms) ## Verifying the fix [Section titled “Verifying the fix”](#verifying-the-fix) After applying the configuration: 1. **Restart your application** - Spring Security initializes the JWT decoder on startup 2. **Test authentication** - Make a request with a valid Scalekit JWT token 3. **Check logs** - You should see successful JWKS retrieval: ```plaintext 1 DEBUG o.s.security.oauth2.jwt.JwtDecoder - Retrieved JWKS from https://auth.scalekit.com/.well-known/jwks.json ``` If you still see timeout errors: * Verify network connectivity to `auth.scalekit.com` * Check firewall rules allowing outbound HTTPS * Increase timeout values if your network has high latency ## When to use standard Spring Security instead [Section titled “When to use standard Spring Security instead”](#when-to-use-standard-spring-security-instead) This cookbook addresses a specific Spring Boot 4.0+ timeout issue. For general JWT validation setup: * Follow the [Spring Security OAuth2 Resource Server documentation](https://docs.spring.io/spring-security/reference/servlet/oauth2/resource-server/jwt.html) * Use Scalekit’s standard Java SDK for token validation if not using Spring Security * Consider the default `issuer-uri` configuration if you’re not experiencing timeouts ## Related resources [Section titled “Related resources”](#related-resources) * [Spring Security OAuth2 Resource Server - JWT Timeouts](https://docs.spring.io/spring-security/reference/servlet/oauth2/resource-server/jwt.html#oauth2resourceserver-jwt-timeouts) * [Scalekit API reference](/apis/#tag/authentication) * [Spring Boot 4.0 Release Notes](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Release-Notes) --- # DOCUMENT BOUNDARY --- # Triage a Gmail inbox with AgentKit and the LiteLLM gateway > Node.js inbox triage agent: classify Gmail threads, route to GitHub repos, draft issues and replies via LiteLLM, and approve before any side effects. Build an automated inbox triage agent that reads your Gmail, classifies each thread, routes it to the right GitHub repository, and notifies Slack — then waits for your approval before creating issues or sending replies. This Node.js sample uses **Scalekit AgentKit** for OAuth tool execution (Gmail, GitHub, Slack) and a **LiteLLM gateway** for model-per-stage routing. The only LiteLLM-specific config is `LITELLM_BASE_URL` and a virtual API key from the dashboard. The sample repository is **[litellm-agentkit-inbox-triage](https://github.com/scalekit-developers/litellm-agentkit-inbox-triage)** on GitHub. ## What you are building [Section titled “What you are building”](#what-you-are-building) * **Gmail ingestion** — Poll for new threads using AgentKit-executed Gmail tools. A SQLite cursor prevents duplicate processing. * **Model-per-stage routing** — Each stage (`classify`, `research`, `tiebreak`, `draft`) calls the LiteLLM gateway with a different model name. Stage-to-model assignments live in `routing.yaml` at the repo root. * **Deterministic GitHub routing** — Keyword rules in `routing.yaml` pick a target repository; an optional LLM tie-breaker resolves ties. * **Research loop** — A small tool-calling loop searches related GitHub issues through AgentKit. * **Slack notification** — Posts a summary with a link to the pending decision. * **Human approval** — A localhost dashboard lists proposals. **Approve** creates the GitHub issue, sends the Gmail reply, and updates Slack. **Reject** discards without side effects. ## Automated triage pipeline [Section titled “Automated triage pipeline”](#automated-triage-pipeline) New Gmail threads flow through AgentKit into a multi-stage LiteLLM pipeline, then land in SQLite as pending proposals. ## Human approval loop [Section titled “Human approval loop”](#human-approval-loop) Proposals wait in SQLite until you review them from the dashboard. ## Prerequisites [Section titled “Prerequisites”](#prerequisites) * A Scalekit account at [app.scalekit.com](https://app.scalekit.com). * Ability to create **AgentKit connections** for **Gmail**, **GitHub**, and **Slack**. Connection **names** must match what you put in `.env` (see [Configure a connection](/agentkit/connections/)). * A **virtual LiteLLM API key** from the dashboard (**LLM Gateway**). A small spend cap of roughly two US dollars covers a handful of test threads. * **Node.js 24 or newer** and **npm**. * An **interactive terminal** — the sample prints authorization links and waits for Enter after each connector. This recipe does not cover headless CI. 1. ## Clone the sample [Section titled “Clone the sample”](#clone-the-sample) ```bash 1 git clone https://github.com/scalekit-developers/litellm-agentkit-inbox-triage.git 2 cd litellm-agentkit-inbox-triage ``` 2. ## Configure AgentKit connections [Section titled “Configure AgentKit connections”](#configure-agentkit-connections) 1. Open [app.scalekit.com](https://app.scalekit.com) → **AgentKit** → **Connections** → **Create Connection** for **Gmail**, **GitHub**, and **Slack**. 2. Copy each **Connection name** exactly as shown in the dashboard into `GMAIL_CONNECTION_NAME`, `GITHUB_CONNECTION_NAME`, and `SLACK_CONNECTION_NAME` in your `.env` file. 3. For **GitHub**, confirm the connection includes the **`repo`** OAuth scope (needed to create issues and search across repositories). Check **AgentKit → Connections → GitHub → Scopes** in the dashboard. See [Scopes and permissions](/agentkit/authentication/scopes-permissions/) and the [GitHub connector](/agentkit/connectors/github/). 4. For **Gmail** and **Slack**, follow the dashboard wizard. If your workspace restricts OAuth apps, see the connector docs: [Gmail](/agentkit/connectors/gmail/), [Slack](/agentkit/connectors/slack/). Dashboard only loads after all three connectors are active The sample calls `setupConnectors` **before** it binds the Express dashboard. You will **not** reach `http://localhost:3000` until Gmail, GitHub, and Slack each show **connector active** in the logs. 3. ## Create a LiteLLM virtual key and verify the gateway [Section titled “Create a LiteLLM virtual key and verify the gateway”](#create-a-litellm-virtual-key-and-verify-the-gateway) Open **LLM Gateway** in the Scalekit dashboard and create a **virtual API key** (optionally set a small budget cap for evaluation). Verify the gateway responds before continuing (load your `.env` first with `set -a && source .env && set +a`): ```bash 1 curl -H "Authorization: Bearer $LITELLM_API_KEY" \ 2 "$LITELLM_BASE_URL/v1/models" ``` Align `routing.yaml` → `models:` with the model IDs returned by that endpoint. 4. ## Configure and run the sample [Section titled “Configure and run the sample”](#configure-and-run-the-sample) Set these variables in `.env` before running: | Variable | Where to find it | | ------------------------ | ---------------------------------------------------- | | `SCALEKIT_ENV_URL` | Dashboard → **Settings** → Environment URL | | `SCALEKIT_CLIENT_ID` | Dashboard → **API Credentials** | | `SCALEKIT_CLIENT_SECRET` | Dashboard → **API Credentials** | | `GMAIL_CONNECTION_NAME` | Dashboard → **AgentKit → Connections** (exact label) | | `GITHUB_CONNECTION_NAME` | Same | | `SLACK_CONNECTION_NAME` | Same | | `LITELLM_BASE_URL` | Dashboard → **LLM Gateway** → Base URL | | `LITELLM_API_KEY` | Dashboard → **LLM Gateway** → virtual key value | ```bash 1 cp .env.example .env 2 # Fill in the variables above 3 4 npm install 5 npm run dev ``` Complete each printed **authorization URL** in the browser, then press **Enter** in the terminal after each connector. When you see **All connectors active** and **dashboard listening on `http://localhost:3000`**, send a test email to the connected Gmail account. Within roughly one poll interval (default **5 seconds**), a proposal appears in the dashboard. 5. ## Approve or reject [Section titled “Approve or reject”](#approve-or-reject) Open **`http://localhost:3000`**. Review the classification, routed repository, related issues, and drafts. **Approve** runs GitHub issue creation, sends the Gmail reply, and updates Slack. **Reject** leaves external systems unchanged. 6. ## Extend the sample [Section titled “Extend the sample”](#extend-the-sample) To add routing targets or swap models per stage, edit `routing.yaml` — each entry maps keyword rules to a GitHub repository and assigns a model name to each pipeline stage. To add connectors, follow the [AgentKit connections guide](/agentkit/connections/) and add the new connection name to `.env`. ## Related resources [Section titled “Related resources”](#related-resources) | Topic | Link | | ----------------------------- | --------------------------------------------------------------- | | AgentKit overview | [Overview](/agentkit/overview/) | | Connections | [Configure a connection](/agentkit/connections/) | | Authorization links | [Authorize a user](/agentkit/tools/authorize/) | | Connected accounts | [Manage connected accounts](/agentkit/connected-accounts/) | | LiteLLM virtual keys | [Virtual keys](https://docs.litellm.ai/docs/proxy/virtual_keys) | | LiteLLM model routing | [Router](https://docs.litellm.ai/docs/routing) | | LiteLLM OpenAI-compatible API | [Proxy usage](https://docs.litellm.ai/docs/proxy/user_keys) | ## Common scenarios [Section titled “Common scenarios”](#common-scenarios) For deeper debugging patterns, see [Authentication troubleshooting](/agentkit/authentication/troubleshooting/). --- # DOCUMENT BOUNDARY --- # M2M JWT verification with JWKS and OAuth scopes > How JSON Web Key Sets work with Scalekit, how to use the /keys endpoint to verify machine-to-machine tokens, and how OAuth scopes map to JWT claims for authorization. When you add OAuth 2.0 client credentials for your APIs, callers receive **JWT access tokens**. Before you trust any claim, you must **verify the signature** using Scalekit’s public keys (**JWKS**). After verification, you **authorize** the request—often by checking **OAuth scopes** carried in the token. This cookbook explains how JWKS and scopes fit together for Scalekit M2M flows: where keys live, how verification works at a high level, how scopes are defined and stored, and how to enforce them in your service. ## Why JWKS and scopes belong in one place [Section titled “Why JWKS and scopes belong in one place”](#why-jwks-and-scopes-belong-in-one-place) * **JWKS answers “is this token real?”** — You use the key identified by `kid` in the JWT header to validate the signature (typically **RS256**). * **Scopes answer “what may this client do?”** — After the token is valid, you inspect the `scopes` claim (and your routing rules) to allow or deny the operation. Skipping either step breaks your security model: verified-but-overpowered clients, or unverified tokens. ## JWKS and Scalekit keys [Section titled “JWKS and Scalekit keys”](#jwks-and-scalekit-keys) A **JSON Web Key Set (JWKS)** is JSON that lists one or more **JWKs**—public key material identified by a `kid` (key ID). Scalekit puts the matching `kid` in the JWT header so your validator can pick the right key without baking certificates into your app. Each environment publishes signing keys at: ```http 1 GET https:///keys ``` Use the same base URL as `/oauth/token` (for example `https://your-app.scalekit.dev`). Example response shape: Example JWKS document ```json 1 { 2 "keys": [ 3 { 4 "use": "sig", 5 "kty": "RSA", 6 "kid": "snk_58327480989122566", 7 "alg": "RS256", 8 "n": "…", 9 "e": "AQAB" 10 } 11 ] 12 } ``` For access tokens, use the key where `use` is `sig` and `alg` is `RS256`. ## Verify an access token [Section titled “Verify an access token”](#verify-an-access-token) At implementation time, your API typically: 1. **Extracts** the bearer token from `Authorization: Bearer `. 2. **Decodes** the JWT header (base64url, first segment) and reads `kid` and `alg`. Do not trust the payload until the signature checks out. 3. **Resolves the signing key** — fetch `https:///keys`, or use a JWKS client (for example `jwks-rsa` in Node.js) with **caching** and refresh when you see an unknown `kid`. 4. **Verifies** the signature with your JWT library (RS256 for Scalekit access tokens). 5. **Validates claims** such as `exp`, `iss` (your environment URL), and `aud` if your API relies on audience restrictions. 6. **Authorizes** the operation using application claims—especially **`scopes`** (covered in the next section). ### Operational practices [Section titled “Operational practices”](#operational-practices) * **Cache JWKS** responses; refetch when verification fails with an unknown `kid` (key rotation). * **Fail closed** on bad signature, wrong issuer, or expired token (`401`; use `403` when the token is valid but not allowed). * **Never** skip signature verification based on the payload alone. ## OAuth scopes for machine clients [Section titled “OAuth scopes for machine clients”](#oauth-scopes-for-machine-clients) **Scopes** are permission names you attach to an OAuth client. In M2M flows they describe *what* a client may do—separate from *who* it is (`client_id` / `sub`). ### Why scopes matter [Section titled “Why scopes matter”](#why-scopes-matter) Without scopes, any valid client could hit any endpoint. Scopes let you apply **least privilege**, **document** what each integration is for, and **enforce** rules in your API by reading the `scopes` array on the JWT. ### How scopes work in Scalekit M2M [Section titled “How scopes work in Scalekit M2M”](#how-scopes-work-in-scalekit-m2m) 1. When you **register an API client** for an organization, you pass a `scopes` array (REST or SDKs). 2. Scalekit stores those scopes and includes them on issued access tokens. 3. Your API **verifies the JWT** (steps above), then checks that `scopes` includes what the route requires. Use a consistent naming pattern such as `resource:action` (for example `deployments:read`, `deployments:write`). ### Register scopes on the client [Section titled “Register scopes on the client”](#register-scopes-on-the-client) Scopes are set at **client creation** (and when you update the client via the API). Example: scopes on create client (illustrative) ```json 1 "scopes": [ 2 "deploy:applications", 3 "read:deployments" 4 ] ``` The same values appear on the client record and in issued tokens. ### Validate scopes on your API [Section titled “Validate scopes on your API”](#validate-scopes-on-your-api) After the token is verified: * **Read `scopes`** from the payload, for example: scopes in JWT payload (example) ```json 1 "scopes": [ 2 "deploy:applications", 3 "read:deployments" 4 ] ``` * **Compare** what the token grants to what the route allows (for example require `deploy:applications` on `POST /deploy`); return `403` if a required scope is missing. * **Use SDK helpers** where they fit your stack to combine signature and scope checks (see the [quickstart](/authenticate/m2m/api-auth-quickstart/)). ## Related [Section titled “Related”](#related) * [Add OAuth 2.0 to your APIs](/authenticate/m2m/api-auth-quickstart/) — client registration, tokens, examples * [API keys](/authenticate/m2m/api-keys/) — long-lived keys; patterns may differ from OAuth client credentials * [Authenticate customer apps](/guides/m2m/api-auth-m2m-clients/) — customer-facing API auth and JWKS examples --- # DOCUMENT BOUNDARY --- # Build a multi-user GitHub PR summarizer agent > Build a GitHub PR summarizer that binds each connected GitHub account to a secure browser session instead of trusting a client-supplied user ID. This recipe builds a GitHub PR summarizer with a browser UI and a secure connected-account flow. Each user connects GitHub once, then the app reuses that connected token for later PR summary requests in the same browser session. The important security rule is straightforward: **never accept a user ID from the browser and use it as the Scalekit connected-account identifier**. Instead, mint an opaque identifier on the server, store it in your own session store, and complete the flow with [user verification for connected accounts](/agentkit/user-verification/). The finished app does four things: * lists the most-discussed open pull requests in a repository * fetches each PR’s diff and comment thread through Scalekit’s GitHub connector * asks an LLM to summarize the PRs in plain language * binds every GitHub connection to a secure browser session instead of a client-supplied identifier The complete source is available in the [render-ai-agent-deploykit](https://github.com/scalekit-developers/render-ai-agent-deploykit) repository. You can also [watch the video walkthrough](https://youtu.be/w3atzSkKE1w) to see the full setup and demo end-to-end. ## What you are building [Section titled “What you are building”](#what-you-are-building) The app runs as a Node web service on Render. It serves an HTML page with a **Connect GitHub** button and a form for `owner` and `repo`. Under the hood, the flow looks like this: ```text 1 Browser (original tab) Browser (new tab) 2 │ │ 3 ▼ GET / │ 4 Express server sets signed session cookie │ 5 │ │ 6 ▼ POST /api/auth │ 7 Scalekit returns GitHub auth link │ 8 │ │ 9 │ opens auth link ─────────────────► ▼ 10 │ GitHub OAuth consent 11 │ │ 12 │ polls GET /api/auth/status ▼ 13 │ ◄─── Scalekit API: ACTIVE ──► Scalekit verifies account 14 │ 15 ▼ page auto-reloads 16 │ 17 ▼ POST /api/summarize { repository } 18 Scalekit runs GitHub requests with the connected user's token ``` The OAuth flow opens in a **new tab** so the app page stays intact. The original tab polls the Scalekit API until the connected account becomes `ACTIVE`, then auto-reloads to show the connected state. ## 1. Set up the GitHub connector [Section titled “1. Set up the GitHub connector”](#1-set-up-the-github-connector) Create the connector once per Scalekit environment. 1. Go to [app.scalekit.com](https://app.scalekit.com) → **AgentKit** > **Connections** > **Create Connection** 2. Find **GitHub** and click **Create** 3. Follow the setup — Scalekit creates and manages the GitHub OAuth app for you 4. Note the **connection name** assigned (e.g. `github-qkHFhMip`) — you’ll set this as `GITHUB_CONNECTION_NAME` in your environment Connection names are unique per environment Scalekit generates a unique GitHub connection name for each environment. Do not copy one from a tutorial or another project. Always use the exact value from your own Scalekit Dashboard. ## 2. Configure user verification (required) [Section titled “2. Configure user verification (required)”](#2-configure-user-verification-required) Scalekit’s user verification setting controls what happens after a user completes GitHub OAuth. **You must choose a mode in the dashboard before the app will work end-to-end.** Go to **AgentKit > Settings > User verification** in the [Scalekit dashboard](https://app.scalekit.com). | Mode | When to use | What happens after OAuth | | ---------------------------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Scalekit users only** | Development and testing | Scalekit verifies the user internally. The connected account goes `ACTIVE` automatically. The app detects this by polling the Scalekit API. | | **Custom user verification** | Production | Scalekit redirects the browser to your app’s `/user/verify` callback. The server calls `verifyConnectedAccountUser` to activate the account. The app also polls the Scalekit API as a fallback. | The app works in **both modes** without code changes. If you skip this step entirely, the connected account may never reach `ACTIVE` status and the app will stay stuck on “Waiting for GitHub authorization.” This step is required This is the most common setup mistake. If you deploy the app, set all environment variables, and complete GitHub OAuth but the app never shows “GitHub connected,” check this dashboard setting first. For the full verification model, see [user verification for connected accounts](/agentkit/user-verification/). ## 3. Create the project [Section titled “3. Create the project”](#3-create-the-project) Terminal ```bash 1 mkdir render-pr-summarizer && cd render-pr-summarizer 2 npm init -y 3 npm install @renderinc/sdk @scalekit-sdk/node openai dotenv express 4 npm install -D typescript tsx @types/node @types/express ``` package.json ```json 1 { 2 "type": "module", 3 "scripts": { 4 "dev": "tsx src/main.ts", 5 "build": "tsc", 6 "start": "node dist/main.js" 7 } 8 } ``` tsconfig.json ```json 1 { 2 "compilerOptions": { 3 "target": "ES2022", 4 "module": "NodeNext", 5 "moduleResolution": "NodeNext", 6 "outDir": "dist", 7 "strict": true 8 }, 9 "include": ["src"] 10 } ``` ## 4. Configure environment variables [Section titled “4. Configure environment variables”](#4-configure-environment-variables) Terminal ```bash 1 cp .env.example .env ``` .env ```bash 1 PORT=3000 2 SESSION_SECRET=replace-with-openssl-rand-hex-32 3 4 OPENAI_API_KEY=your-api-key 5 OPENAI_MODEL=gpt-4.1-mini 6 # Leave OPENAI_BASE_URL empty for OpenAI direct. 7 # Set it to a proxy URL for LiteLLM, Azure OpenAI, Ollama, etc. 8 # OPENAI_BASE_URL=https://your-litellm-proxy.example.com 9 10 SCALEKIT_ENVIRONMENT_URL=https://your-env.scalekit.com 11 SCALEKIT_CLIENT_ID=your-scalekit-client-id 12 SCALEKIT_CLIENT_SECRET=your-scalekit-client-secret 13 GITHUB_CONNECTION_NAME=your-github-connection-name 14 15 # Optional — the app auto-detects its public URL from proxy headers. 16 # Only set this if you need to pin the callback origin explicitly. 17 # PUBLIC_BASE_URL=http://localhost:3000 ``` Generate `SESSION_SECRET` with: Terminal ```bash 1 openssl rand -hex 32 ``` ## 5. Add Scalekit auth helpers [Section titled “5. Add Scalekit auth helpers”](#5-add-scalekit-auth-helpers) The helper layer creates connected accounts, generates auth links, verifies the callback, and routes GitHub API calls through Scalekit’s connector. src/scalekit.ts ```typescript 1 import "dotenv/config"; 2 import { ScalekitClient } from "@scalekit-sdk/node"; 3 import type { JsonObject } from "@bufbuild/protobuf"; 4 5 let _scalekit: ScalekitClient | null = null; 6 7 function getScalekit(): ScalekitClient { 8 if (_scalekit) return _scalekit; 9 if (!process.env.SCALEKIT_ENVIRONMENT_URL || !process.env.SCALEKIT_CLIENT_ID || !process.env.SCALEKIT_CLIENT_SECRET) { 10 throw new Error("Missing SCALEKIT_ENVIRONMENT_URL, SCALEKIT_CLIENT_ID, or SCALEKIT_CLIENT_SECRET"); 11 } 12 _scalekit = new ScalekitClient( 13 process.env.SCALEKIT_ENVIRONMENT_URL, 14 process.env.SCALEKIT_CLIENT_ID, 15 process.env.SCALEKIT_CLIENT_SECRET, 16 ); 17 return _scalekit; 18 } 19 20 export const scalekit = new Proxy({} as ScalekitClient, { 21 get(_target, prop) { 22 return (getScalekit() as unknown as Record)[prop]; 23 }, 24 }); 25 26 const GITHUB_CONNECTION_NAME = process.env.GITHUB_CONNECTION_NAME; 27 if (!GITHUB_CONNECTION_NAME) { 28 throw new Error( 29 "GITHUB_CONNECTION_NAME is required. Copy the connection name from Scalekit Dashboard > Agent Auth > Connectors.", 30 ); 31 } 32 33 export async function getGitHubAuthLink( 34 identifier: string, 35 opts: { state: string; userVerifyUrl: string }, 36 ): Promise { 37 await scalekit.actions.getOrCreateConnectedAccount({ 38 connectionName: GITHUB_CONNECTION_NAME, 39 identifier, 40 }); 41 42 const res = await scalekit.actions.getAuthorizationLink({ 43 connectionName: GITHUB_CONNECTION_NAME, 44 identifier, 45 state: opts.state, 46 userVerifyUrl: opts.userVerifyUrl, 47 }); 48 49 if (!res.link) { 50 throw new Error( 51 `Scalekit did not return a GitHub authorization link for '${GITHUB_CONNECTION_NAME}' and identifier '${identifier}'`, 52 ); 53 } 54 55 return res.link; 56 } 57 58 export async function verifyUser(params: { 59 authRequestId: string; 60 identifier: string; 61 }): Promise { 62 await scalekit.actions.verifyConnectedAccountUser({ 63 authRequestId: params.authRequestId, 64 identifier: params.identifier, 65 }); 66 } 67 68 /** 69 * Check the connected account status via Scalekit API. 70 * Returns true when the account is active (OAuth complete and verified). 71 */ 72 export async function isAccountActive(identifier: string): Promise { 73 try { 74 const res = await scalekit.actions.getConnectedAccount({ 75 connectionName: GITHUB_CONNECTION_NAME, 76 identifier, 77 }); 78 // ConnectorStatus.ACTIVE === 1 79 return res.connectedAccount?.status === 1; 80 } catch { 81 return false; 82 } 83 } 84 85 export async function githubTool( 86 identifier: string, 87 toolName: string, 88 toolInput: Record, 89 ): Promise { 90 const res = await scalekit.actions.executeTool({ 91 toolName, 92 toolInput, 93 connector: GITHUB_CONNECTION_NAME, 94 identifier, 95 }); 96 97 return res.data ?? {}; 98 } 99 100 export async function githubRequest( 101 identifier: string, 102 path: string, 103 options: { 104 method?: string; 105 headers?: Record; 106 queryParams?: Record; 107 } = {}, 108 ) { 109 const res = await scalekit.actions.request({ 110 connectionName: GITHUB_CONNECTION_NAME, 111 identifier, 112 path, 113 method: options.method ?? "GET", 114 headers: options.headers, 115 queryParams: options.queryParams, 116 }); 117 118 return res.data; 119 } ``` Use the exact connector name The `connector` value in `executeTool` must be the full connection name from your own Scalekit environment, not the generic provider string `"github"`. ## 6. Bind the browser session to an opaque identifier [Section titled “6. Bind the browser session to an opaque identifier”](#6-bind-the-browser-session-to-an-opaque-identifier) The session layer is the security boundary for the whole app. Create `src/session.ts` and store three things: * a signed session cookie sent to the browser * an opaque `usr_...` identifier stored on the server * a one-time `state` value stored on the server while OAuth is in flight src/session.ts ```typescript 1 import { createHmac, randomBytes, timingSafeEqual } from "node:crypto"; 2 import type { Request, Response } from "express"; 3 4 const COOKIE_NAME = "sid"; 5 const STATE_TTL_MS = 10 * 60 * 1000; 6 7 interface SessionEntry { 8 identifier: string; 9 pendingState?: string; 10 pendingStateExpiresAt?: number; 11 connectedAt?: number; 12 } 13 14 const store = new Map(); 15 16 function getSecret(): string { 17 const secret = process.env.SESSION_SECRET; 18 if (!secret) { 19 throw new Error("SESSION_SECRET is required"); 20 } 21 return secret; 22 } 23 24 function sign(sessionId: string): string { 25 const mac = createHmac("sha256", getSecret()).update(sessionId).digest("base64url"); 26 return `${sessionId}.${mac}`; 27 } 28 29 function unsign(signed: string): string | null { 30 const dot = signed.lastIndexOf("."); 31 if (dot < 0) return null; 32 33 const sessionId = signed.slice(0, dot); 34 const mac = signed.slice(dot + 1); 35 const expected = createHmac("sha256", getSecret()).update(sessionId).digest("base64url"); 36 37 const expectedBuf = Buffer.from(expected); 38 const macBuf = Buffer.from(mac); 39 if (expectedBuf.length !== macBuf.length) return null; 40 41 return timingSafeEqual(expectedBuf, macBuf) ? sessionId : null; 42 } 43 44 export function requireSession(req: Request, res: Response) { 45 const cookies = Object.fromEntries( 46 (req.headers.cookie ?? "") 47 .split(";") 48 .flatMap((pair) => { 49 const eq = pair.indexOf("="); 50 if (eq < 0) return []; 51 try { 52 return [[pair.slice(0, eq).trim(), decodeURIComponent(pair.slice(eq + 1).trim())]]; 53 } catch { 54 return []; 55 } 56 }), 57 ); 58 59 const raw = cookies[COOKIE_NAME]; 60 let sessionId = raw ? unsign(raw) : null; 61 let entry = sessionId ? store.get(sessionId) ?? null : null; 62 63 if (!sessionId || !entry) { 64 sessionId = randomBytes(32).toString("base64url"); 65 entry = { identifier: "" }; 66 store.set(sessionId, entry); 67 } 68 69 // The cookie only carries a random opaque session id. HMAC signing is enough 70 // to detect tampering because the sensitive identifier stays server-side. 71 const protoHeader = req.get("x-forwarded-proto"); 72 const requestIsSecure = req.secure || protoHeader?.split(",")[0]?.trim() === "https"; 73 const secure = 74 process.env.NODE_ENV === "production" || 75 process.env.PUBLIC_BASE_URL?.startsWith("https://") === true || 76 requestIsSecure; 77 const parts = [ 78 `${COOKIE_NAME}=${sign(sessionId)}`, 79 "HttpOnly", 80 "SameSite=Lax", 81 "Path=/", 82 `Max-Age=${7 * 24 * 60 * 60}`, 83 ]; 84 if (secure) parts.push("Secure"); 85 res.setHeader("Set-Cookie", parts.join("; ")); 86 87 return { entry }; 88 } 89 90 export function mintIdentifier(entry: SessionEntry): string { 91 if (!entry.identifier) { 92 entry.identifier = `usr_${randomBytes(16).toString("hex")}`; 93 } 94 return entry.identifier; 95 } 96 97 export function setPendingState(entry: SessionEntry, state: string): void { 98 entry.pendingState = state; 99 entry.pendingStateExpiresAt = Date.now() + STATE_TTL_MS; 100 } 101 102 export function consumePendingState(entry: SessionEntry, incoming: string): boolean { 103 const stored = entry.pendingState; 104 const expiresAt = entry.pendingStateExpiresAt; 105 entry.pendingState = undefined; 106 entry.pendingStateExpiresAt = undefined; 107 108 if (!stored || !expiresAt || Date.now() > expiresAt) return false; 109 110 const storedBuf = Buffer.from(stored); 111 const incomingBuf = Buffer.from(incoming); 112 if (storedBuf.length !== incomingBuf.length) return false; 113 114 return timingSafeEqual(storedBuf, incomingBuf); 115 } 116 117 export function markConnected(entry: SessionEntry): void { 118 entry.connectedAt = Date.now(); 119 } 120 121 export function isConnected(entry: SessionEntry): boolean { 122 return entry.connectedAt !== undefined; 123 } ``` Never trust query params for identity Read the identifier from your own session store, not from the URL and not from the request body. The callback query string only proves that Scalekit completed an OAuth flow. Your server must decide which local user session owns that new connection. ## 7. Add the tasks [Section titled “7. Add the tasks”](#7-add-the-tasks) The task layer now accepts a server-side `identifier`, not a browser-supplied `userId`. src/tasks.ts ```typescript 1 import { task } from "@renderinc/sdk/workflows"; 2 import OpenAI from "openai"; 3 import { githubRequest, githubTool, getGitHubAuthLink } from "./scalekit.js"; 4 5 export interface PRSummaryInput { 6 identifier: string; 7 owner: string; 8 repo: string; 9 } 10 11 const fetchOpenPRs = task( 12 { name: "fetchOpenPRs", retry: { maxRetries: 3, waitDurationMs: 1000 } }, 13 async function fetchOpenPRs(identifier: string, owner: string, repo: string) { 14 const raw = await githubTool(identifier, "github_pull_requests_list", { 15 owner, 16 repo, 17 state: "open", 18 }); 19 20 const r = raw as Record; 21 const list = Array.isArray(raw) 22 ? raw 23 : Array.isArray(r.array) ? r.array 24 : Array.isArray(r.pull_requests) ? r.pull_requests 25 : Array.isArray(r.data) ? r.data 26 : null; 27 28 if (!list) { 29 throw new Error(`Unexpected response shape: ${JSON.stringify(raw).slice(0, 200)}`); 30 } 31 32 type PRItem = { number: number; title: string; comments: number; review_comments: number }; 33 return (list as PRItem[]) 34 .sort((a, b) => (b.comments + b.review_comments) - (a.comments + a.review_comments)) 35 .slice(0, 5); 36 }, 37 ); 38 39 const fetchPRDetails = task( 40 { name: "fetchPRDetails", retry: { maxRetries: 3, waitDurationMs: 1000 } }, 41 async function fetchPRDetails(identifier: string, owner: string, repo: string, prNumber: number) { 42 const [diffRaw, commentsRaw] = await Promise.all([ 43 githubRequest(identifier, `/repos/${owner}/${repo}/pulls/${prNumber}`, { 44 headers: { Accept: "application/vnd.github.diff" }, 45 }), 46 githubRequest(identifier, `/repos/${owner}/${repo}/issues/${prNumber}/comments`), 47 ]); 48 49 const diff = typeof diffRaw === "string" ? diffRaw.slice(0, 3000) : ""; 50 const comments = Array.isArray(commentsRaw) ? commentsRaw : []; 51 52 return { diff, comments }; 53 }, 54 ); 55 56 export const setupGitHubAuthTask = task( 57 { name: "setupGitHubAuth" }, 58 async function setupGitHubAuth(params: { 59 identifier: string; 60 state: string; 61 userVerifyUrl: string; 62 }) { 63 const link = await getGitHubAuthLink(params.identifier, { 64 state: params.state, 65 userVerifyUrl: params.userVerifyUrl, 66 }); 67 68 return { authLink: link }; 69 }, 70 ); 71 72 // ---- LLM summary ---- 73 74 function createOpenAIClient(): OpenAI { 75 const apiKey = process.env.OPENAI_API_KEY; 76 if (!apiKey) throw new Error("OPENAI_API_KEY not set"); 77 return new OpenAI({ 78 apiKey, 79 ...(process.env.OPENAI_BASE_URL && { baseURL: process.env.OPENAI_BASE_URL }), 80 }); 81 } 82 83 const generateSummary = task( 84 { name: "generateSummary", retry: { maxRetries: 3, waitDurationMs: 2000 } }, 85 async function generateSummary( 86 prs: { number: number; title: string; diff: string; comments: { body?: string }[] }[], 87 owner: string, 88 repo: string, 89 ): Promise { 90 if (prs.length === 0) return "No open pull requests found in this repository."; 91 92 const client = createOpenAIClient(); 93 const prBlocks = prs 94 .map((pr) => { 95 const bodies = pr.comments.slice(0, 5).map((c) => `> ${(c.body ?? "").slice(0, 300)}`).join("\n"); 96 return `PR #${pr.number} — ${pr.title}\n${bodies || "No comments."}\nDiff:\n${pr.diff || "(not available)"}`; 97 }) 98 .join("\n\n---\n\n"); 99 100 const response = await client.chat.completions.create({ 101 model: process.env.OPENAI_MODEL ?? "gpt-4.1-mini", 102 messages: [ 103 { 104 role: "system", 105 content: 106 "Summarize each PR in one paragraph (3-4 sentences) for a team lead. " + 107 "Cover what it does, how much discussion happened, and whether it looks close to merging.", 108 }, 109 { role: "user", content: `Repository: ${owner}/${repo}\n\n${prBlocks}` }, 110 ], 111 }); 112 113 return response.choices[0].message.content ?? "(no summary generated)"; 114 }, 115 ); 116 117 // ---- Root task ---- 118 119 export const summarizePRsTask = task( 120 { name: "summarizePRs", timeoutSeconds: 120 }, 121 async function summarizePRs(input: PRSummaryInput) { 122 const { identifier, owner, repo } = input; 123 const topPRs = await fetchOpenPRs(identifier, owner, repo); 124 125 if (topPRs.length === 0) { 126 return { repository: `${owner}/${repo}`, prsAnalyzed: [] as string[], summary: "No open pull requests found." }; 127 } 128 129 const details = await Promise.all( 130 topPRs.map((pr) => fetchPRDetails(identifier, owner, repo, pr.number)), 131 ); 132 133 const prsForSummary = topPRs.map((pr, i) => ({ 134 number: pr.number, 135 title: pr.title, 136 diff: details[i].diff, 137 comments: details[i].comments as { body?: string }[], 138 })); 139 140 const summary = await generateSummary(prsForSummary, owner, repo); 141 142 return { 143 repository: `${owner}/${repo}`, 144 prsAnalyzed: topPRs.map((p) => `#${p.number}: ${p.title}`), 145 summary, 146 }; 147 }, 148 ); ``` ## 8. Wire the HTTP server [Section titled “8. Wire the HTTP server”](#8-wire-the-http-server) The HTTP server owns the secure flow. It issues the session cookie, starts the GitHub auth flow, validates the callback, and blocks summary requests until the session is connected. src/server.ts ```typescript 1 import crypto from "node:crypto"; 2 import express from "express"; 3 import { setupGitHubAuthTask, summarizePRsTask } from "./tasks.js"; 4 import { isAccountActive, verifyUser } from "./scalekit.js"; 5 import { 6 consumePendingState, 7 isConnected, 8 markConnected, 9 mintIdentifier, 10 requireSession, 11 setPendingState, 12 } from "./session.js"; 13 import { renderHomePage, renderAuthCompletePage } from "./views.js"; 14 import type { Request } from "express"; 15 16 function getConfiguredPublicBaseUrl(): string | null { 17 const value = process.env.PUBLIC_BASE_URL; 18 return value ? value.replace(/\/$/, "") : null; 19 } 20 21 function getRequestOrigin(req: Request): string { 22 const configured = getConfiguredPublicBaseUrl(); 23 if (configured) return configured; 24 25 const protoHeader = req.get("x-forwarded-proto"); 26 const proto = protoHeader?.split(",")[0]?.trim() || req.protocol || "http"; 27 const host = req.get("x-forwarded-host") || req.get("host"); 28 if (!host) { 29 throw new Error("Could not determine the public origin for this request"); 30 } 31 return `${proto}://${host}`; 32 } 33 34 export function startServer(): void { 35 const app = express(); 36 app.set("trust proxy", true); 37 app.use(express.json()); 38 39 app.get("/", (req, res) => { 40 const { entry } = requireSession(req, res); 41 res.type("html").send(renderHomePage({ connected: isConnected(entry) })); 42 }); 43 44 // Polled by the original tab while the OAuth tab is open. 45 // Checks the in-memory session first, then queries the Scalekit API 46 // to detect when the connected account becomes ACTIVE. 47 app.get("/api/auth/status", async (req, res) => { 48 const { entry } = requireSession(req, res); 49 if (isConnected(entry)) { 50 res.json({ connected: true }); 51 return; 52 } 53 if (entry.identifier && await isAccountActive(entry.identifier)) { 54 markConnected(entry); 55 res.json({ connected: true }); 56 return; 57 } 58 res.json({ connected: false }); 59 }); 60 61 app.post("/api/auth", async (req, res) => { 62 const { entry } = requireSession(req, res); 63 const identifier = mintIdentifier(entry); 64 65 const state = crypto.randomUUID(); 66 setPendingState(entry, state); 67 68 const result = await setupGitHubAuthTask({ 69 identifier, 70 state, 71 userVerifyUrl: `${getRequestOrigin(req)}/user/verify`, 72 }); 73 74 res.json({ authLink: result.authLink }); 75 }); 76 77 // Callback for custom user verification mode. When Scalekit is 78 // configured in "Scalekit users only" mode, this route may not fire — 79 // the /api/auth/status polling handles that case via the Scalekit API. 80 app.get("/user/verify", async (req, res) => { 81 const { auth_request_id, state } = req.query as Record; 82 if (!auth_request_id || !state) { 83 res.status(400).send("Missing auth_request_id or state"); 84 return; 85 } 86 87 const { entry } = requireSession(req, res); 88 if (!entry.identifier) { 89 res.status(400).send("No pending authorization for this session"); 90 return; 91 } 92 93 if (!consumePendingState(entry, state)) { 94 res.status(400).send("Invalid or expired state"); 95 return; 96 } 97 98 await verifyUser({ 99 authRequestId: auth_request_id, 100 identifier: entry.identifier, 101 }); 102 103 markConnected(entry); 104 // This handler runs in the OAuth tab. Render a minimal page 105 // telling the user to close it — the original tab is polling 106 // /api/auth/status and will auto-reload. 107 res.type("html").send(renderAuthCompletePage()); 108 }); 109 110 app.post("/api/summarize", async (req, res) => { 111 const { entry } = requireSession(req, res); 112 if (!isConnected(entry)) { 113 res.status(401).json({ error: "Connect your GitHub account first" }); 114 return; 115 } 116 117 // The UI sends { repository: "https://github.com/owner/repo" } or "owner/repo". 118 // Parse the string into separate owner and repo values. 119 const { repository } = req.body as { repository?: string }; 120 if (!repository) { 121 res.status(400).json({ error: "Provide a GitHub repository URL or owner/repo name." }); 122 return; 123 } 124 125 let owner: string | undefined; 126 let repo: string | undefined; 127 try { 128 const url = new URL(repository); 129 const segments = url.pathname.split("/").filter(Boolean); 130 owner = segments[0]; 131 repo = segments[1]?.replace(/\.git$/, ""); 132 } catch { 133 const parts = repository.split("/"); 134 owner = parts[0]; 135 repo = parts[1]?.replace(/\.git$/, ""); 136 } 137 138 if (!owner || !repo) { 139 res.status(400).json({ error: "Provide a GitHub repository URL or owner/repo name." }); 140 return; 141 } 142 143 const result = await summarizePRsTask({ identifier: entry.identifier, owner, repo }); 144 res.json(result); 145 }); 146 } ``` ## 9. Render the browser UI [Section titled “9. Render the browser UI”](#9-render-the-browser-ui) The UI only asks for a repository. It does not ask for a user identifier. After a successful connection, the page auto-reloads and shows a connected banner. The key change from a naive implementation: `connectGitHub()` opens the auth link in a **new tab** instead of navigating the current page. This keeps the app intact even if the OAuth redirect chain doesn’t return cleanly. The original tab polls `/api/auth/status` and auto-reloads when the Scalekit API reports the account as `ACTIVE`. src/views.ts ```typescript 1 export function renderAuthCompletePage(): string { 2 return ` 3 4 5
6

✓ GitHub connected

7

You can close this tab and return to the app. The original page will update automatically.

8
9 10 `; 11 } 12 13 export function renderHomePage({ connected }: { connected: boolean }): string { 14 const connectedBanner = connected 15 ? `
✓ GitHub connected
` 16 : `
Connect GitHub before summarizing pull requests.
`; 17 const authButtonLabel = connected ? "Reconnect GitHub" : "Connect GitHub"; 18 19 return ` 20 21 22 ${connectedBanner} 23 24
25 26 27
 28  88  89 `; 90 } ``` ## 10. Run locally [Section titled “10. Run locally”](#10-run-locally) 1. Copy `.env.example` to `.env` and fill in your values. 2. Run `npm install`. 3. Run `npm run dev`. 4. Open `http://localhost:3000`. 5. Click **Connect GitHub**. A new tab opens for the GitHub OAuth flow. 6. Complete the OAuth consent in the new tab. 7. The new tab shows “GitHub connected — you can close this tab” (in custom verification mode) or a Scalekit success page (in Scalekit-users-only mode). 8. The original tab auto-detects the connection and reloads, showing a **GitHub connected** banner. 9. Enter a repository URL or `owner/repo`, then generate a summary. Public repositories work with any connected GitHub account. Private repositories only work if the connected account has access. ## 11. Deploy to Render [Section titled “11. Deploy to Render”](#11-deploy-to-render) Render deploys the app as a web service from `render.yaml`. Set these environment variables in Render: | Variable | Required | Notes | | -------------------------- | -------- | --------------------------------------------------------------------- | | `SCALEKIT_ENVIRONMENT_URL` | Yes | From Scalekit dashboard → Developers → API Credentials | | `SCALEKIT_CLIENT_ID` | Yes | Same location | | `SCALEKIT_CLIENT_SECRET` | Yes | Same location | | `GITHUB_CONNECTION_NAME` | Yes | From AgentKit → Connectors | | `OPENAI_API_KEY` | Yes | OpenAI key or proxy token | | `OPENAI_BASE_URL` | No | Leave empty for OpenAI direct. Set for LiteLLM/Azure/Ollama. | | `OPENAI_MODEL` | No | Default: `gpt-4.1-mini` | | `SESSION_SECRET` | Auto | `render.yaml` auto-generates this | | `PUBLIC_BASE_URL` | No | Auto-detected from proxy headers. Only needed behind a custom domain. | After deploying, configure user verification in the Scalekit dashboard ([step 2](#2-configure-user-verification-required)). The app will not complete the GitHub connection flow without this. ## Production notes [Section titled “Production notes”](#production-notes) * **User verification mode**: Switch to **Custom user verification** in the Scalekit dashboard before going to production. This ensures your backend confirms which session owns each new connection. * **Shared session store**: The sample stores session data in memory. Use Redis or a database-backed shared store in production. * **Short-lived OAuth state**: The sample expires the pending `state` after 10 minutes and consumes it after a single callback. * **Session-bound identifier**: The browser never chooses the identifier that Scalekit uses to look up the connected account. * **Connector-backed GitHub requests**: The sample routes both PR listing and PR detail fetches through Scalekit so the connected user’s token is used consistently. ## Next steps [Section titled “Next steps”](#next-steps) * Read [user verification for connected accounts](/agentkit/user-verification/) for the full verification model and additional examples. * Read [authorize a user](/agentkit/tools/authorize/) for the status-polling pattern used to detect when a connected account becomes `ACTIVE`. * Open the [render-ai-agent-deploykit](https://github.com/scalekit-developers/render-ai-agent-deploykit) repository to compare the full implementation against the snippets in this cookbook.

---
# DOCUMENT BOUNDARY
---

# Build an agent that books meetings and drafts emails

> Connect a Python agent to Google Calendar and Gmail via Scalekit to find free slots, book meetings, and draft follow-up emails.

Scheduling a meeting sounds simple: find a free slot, create an event, send a confirmation. But in an agent, each of those steps crosses a tool boundary — and each tool requires its own OAuth token. Without a managed auth layer, you end up writing token-fetching, refresh logic, and error handling three times over before you write a single line of scheduling logic. This cookbook solves that by using Scalekit to own the OAuth lifecycle for each connector, so your agent can focus on the workflow itself. This is a Python recipe for agents that call two or more external APIs on behalf of a user. If you’re using a service account rather than user-delegated OAuth, or building in JavaScript, the pattern is the same but the source differs — see the `javascript/` track in [agent-auth-examples](https://github.com/scalekit-developers/agent-auth-examples). The complete Python source used here is `python/meeting_scheduler_agent.py` in that repo. **The core problems this solves:** * **One token per connector** — Google Calendar and Gmail use separate OAuth scopes and separate access tokens. Your agent must manage both independently. * **First-run authorization is blocking** — If the user has not yet authorized a connector, your agent cannot proceed until they complete the browser OAuth flow. * **Token expiry is silent** — A token that worked yesterday fails today, and the failure looks identical to a permissions error. * **Chaining tool outputs is fragile** — The event link from the Calendar API needs to appear in the Gmail draft. If the Calendar call fails mid-workflow, the draft gets a broken link or never gets created. Scalekit exposes a `connected_accounts` abstraction that maps a user ID to an authorized OAuth session per connector. When your agent calls `get_or_create_connected_account`, Scalekit either returns an existing active account with a valid token or creates a new one and returns an authorization URL. Once the user authorizes, `get_connected_account` returns the token. From that point, Scalekit handles refresh automatically. This means your agent’s authorization step is a single function regardless of which connector you’re targeting. The rest of the code — Calendar queries, event creation, Gmail drafts — is plain HTTP with the token Scalekit provides. 1. **Set up the environment** Create a `.env` file at the project root with your Scalekit credentials: ```bash 1 SCALEKIT_ENVIRONMENT_URL=https://your-env.scalekit.com 2 SCALEKIT_CLIENT_ID=your-client-id 3 SCALEKIT_CLIENT_SECRET=your-client-secret ``` Install dependencies: ```bash 1 pip install scalekit-sdk python-dotenv requests ``` In the Scalekit Dashboard, create two connections for your environment: * `googlecalendar` — Google Calendar OAuth connection * `gmail` — Gmail OAuth connection The script references these names literally. The names must match exactly. 2. **Initialize the Scalekit client** meeting\_scheduler\_agent.py ```python 1 import os 2 import base64 3 from datetime import datetime, timezone, timedelta 4 from email.mime.text import MIMEText 5 6 import requests 7 from dotenv import load_dotenv 8 from scalekit import ScalekitClient 9 10 load_dotenv() 11 12 # Never hard-code credentials — they would be exposed in source control 13 # and CI logs. Pull them from environment variables instead. 14 scalekit_client = ScalekitClient( 15 environment_url=os.getenv("SCALEKIT_ENVIRONMENT_URL"), 16 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 17 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 18 ) 19 20 actions = scalekit_client.actions 21 22 # Replace with a real user identifier from your application's session 23 USER_ID = "user_123" 24 ATTENDEE_EMAIL = "attendee@example.com" 25 MEETING_TITLE = "Quick Sync" 26 DURATION_MINUTES = 60 27 SEARCH_DAYS = 3 28 WORK_START_HOUR = 9 # UTC 29 WORK_END_HOUR = 17 # UTC ``` `scalekit_client.actions` is the entry point for all connected-account operations. Initialize it once and pass `actions` to the functions below. 3. **Authorize each connector** The `authorize` function handles the first-run prompt and returns a valid access token: ```python 1 def authorize(connector: str) -> str: 2 """Ensure the user has an active connected account and return its access token. 3 4 On first run, this prints an authorization URL and waits for the user 5 to complete the browser OAuth flow before continuing. 6 """ 7 account = actions.get_or_create_connected_account(connector, USER_ID) 8 9 if account.status != "active": 10 auth_link = actions.get_authorization_link(connector, USER_ID) 11 print(f"\nOpen this link to authorize {connector}:\n{auth_link}\n") 12 input("Press Enter after completing authorization in your browser…") 13 account = actions.get_connected_account(connector, USER_ID) 14 15 return account.authorization_details["oauth_token"]["access_token"] ``` Call this once per connector before any API calls: ```python 1 calendar_token = authorize("googlecalendar") 2 gmail_token = authorize("gmail") ``` After the first successful authorization, `get_or_create_connected_account` returns `status == "active"` on subsequent runs and the `if` block is skipped. Scalekit refreshes expired tokens automatically. 4. **Query calendar availability** With a valid Calendar token, query the `freeBusy` endpoint to get the user’s busy intervals: ```python 1 def get_busy_slots(token: str) -> list[dict]: 2 """Fetch busy intervals for the user's primary calendar.""" 3 now = datetime.now(timezone.utc) 4 window_end = now + timedelta(days=SEARCH_DAYS) 5 6 response = requests.post( 7 "https://www.googleapis.com/calendar/v3/freeBusy", 8 headers={"Authorization": f"Bearer {token}"}, 9 json={ 10 "timeMin": now.isoformat(), 11 "timeMax": window_end.isoformat(), 12 "items": [{"id": "primary"}], 13 }, 14 ) 15 response.raise_for_status() 16 return response.json()["calendars"]["primary"]["busy"] ``` `raise_for_status()` converts 4xx and 5xx responses into exceptions, so the caller sees a clear error rather than a silent wrong result. The `busy` list contains `{"start": "...", "end": "..."}` dicts in ISO 8601 format. 5. **Find the first open slot** Walk forward in one-hour increments from now and return the first candidate that falls within working hours and does not overlap a busy interval: ```python 1 def find_free_slot(busy_slots: list[dict]) -> tuple[datetime, datetime] | None: 2 """Return the first open one-hour slot during working hours in UTC. 3 4 Returns None if no slot is available in the search window. 5 """ 6 now = datetime.now(timezone.utc) 7 # Round up to the next whole hour so the candidate is always in the future 8 candidate = now.replace(minute=0, second=0, microsecond=0) + timedelta(hours=1) 9 window_end = now + timedelta(days=SEARCH_DAYS) 10 11 while candidate < window_end: 12 slot_end = candidate + timedelta(minutes=DURATION_MINUTES) 13 14 if WORK_START_HOUR <= candidate.hour < WORK_END_HOUR: 15 overlap = any( 16 candidate < datetime.fromisoformat(b["end"]) 17 and slot_end > datetime.fromisoformat(b["start"]) 18 for b in busy_slots 19 ) 20 if not overlap: 21 return candidate, slot_end 22 23 candidate += timedelta(hours=1) 24 25 return None ``` This is a useful first-draft strategy: simple, readable, easy to debug. Its limits are real (one-hour granularity, UTC-only, primary calendar only) and addressed in [Production notes](#production-notes) below. 6. **Create the calendar event** Post the event to the Google Calendar API and return its HTML link, which you’ll include in the email draft: ```python 1 def create_event(token: str, start: datetime, end: datetime) -> str: 2 """Create a calendar event and return its HTML link.""" 3 response = requests.post( 4 "https://www.googleapis.com/calendar/v3/calendars/primary/events", 5 headers={"Authorization": f"Bearer {token}"}, 6 json={ 7 "summary": MEETING_TITLE, 8 "description": "Scheduled by agent", 9 "start": {"dateTime": start.isoformat(), "timeZone": "UTC"}, 10 "end": {"dateTime": end.isoformat(), "timeZone": "UTC"}, 11 "attendees": [{"email": ATTENDEE_EMAIL}], 12 }, 13 ) 14 response.raise_for_status() 15 return response.json()["htmlLink"] ``` The `htmlLink` in the response is the calendar event URL. Google also sends an invitation email to each attendee automatically when the event is created; the draft you create in the next step is a separate follow-up, not the invitation itself. 7. **Draft the confirmation email** Build the email body, base64-encode it, and post it to Gmail’s drafts endpoint: ```python 1 def create_draft(token: str, event_link: str, start: datetime) -> None: 2 """Create a Gmail draft with the meeting details.""" 3 body = ( 4 f"Hi,\n\n" 5 f"I've scheduled '{MEETING_TITLE}' for " 6 f"{start.strftime('%A, %B %d at %H:%M UTC')} ({DURATION_MINUTES} min).\n\n" 7 f"Calendar link: {event_link}\n\n" 8 f"Looking forward to it!" 9 ) 10 11 message = MIMEText(body) 12 message["to"] = ATTENDEE_EMAIL 13 message["subject"] = f"Invitation: {MEETING_TITLE}" 14 15 # Gmail's API requires the raw RFC 2822 message encoded as URL-safe base64 16 raw = base64.urlsafe_b64encode(message.as_bytes()).decode() 17 18 response = requests.post( 19 "https://gmail.googleapis.com/gmail/v1/users/me/drafts", 20 headers={"Authorization": f"Bearer {token}"}, 21 json={"message": {"raw": raw}}, 22 ) 23 response.raise_for_status() 24 print("Draft created in Gmail.") ``` The script creates a draft, not a sent message. The user reviews it before sending. This is the right default for an agent — it takes the action but keeps a human in the loop for outbound communication. 8. **Wire it together** ```python 1 def main() -> None: 2 print("Authorizing Google Calendar…") 3 calendar_token = authorize("googlecalendar") 4 5 print("Authorizing Gmail…") 6 gmail_token = authorize("gmail") 7 8 print("Checking calendar availability…") 9 busy_slots = get_busy_slots(calendar_token) 10 11 slot = find_free_slot(busy_slots) 12 if not slot: 13 print(f"No free slot found in the next {SEARCH_DAYS} days.") 14 return 15 16 start, end = slot 17 print(f"Found slot: {start.strftime('%A %B %d, %H:%M')} UTC") 18 19 print("Creating calendar event…") 20 event_link = create_event(calendar_token, start, end) 21 print(f"Event created: {event_link}") 22 23 print("Creating Gmail draft…") 24 create_draft(gmail_token, event_link, start) 25 26 27 if __name__ == "__main__": 28 main() ``` ## Testing [Section titled “Testing”](#testing) Run the agent from the command line: ```bash 1 python meeting_scheduler_agent.py ``` On first run, you should see two authorization prompts in sequence: ```plaintext 1 Authorizing Google Calendar… 2 3 Open this link to authorize googlecalendar: 4 https://accounts.google.com/o/oauth2/auth?... 5 6 Press Enter after completing authorization in your browser… 7 8 Authorizing Gmail… 9 10 Open this link to authorize gmail: 11 https://accounts.google.com/o/oauth2/auth?... 12 13 Press Enter after completing authorization in your browser… 14 15 Checking calendar availability… 16 Found slot: Wednesday March 11, 10:00 UTC 17 Creating calendar event… 18 Event created: https://calendar.google.com/calendar/event?eid=... 19 Creating Gmail draft… 20 Draft created in Gmail. ``` On subsequent runs, the authorization prompts are skipped and the agent goes straight to availability checking. Verify the results: 1. Open Google Calendar — you should see the event on the chosen date 2. Open Gmail — you should see a draft in the Drafts folder with the event link ## Common mistakes [Section titled “Common mistakes”](#common-mistakes) * **Connection name mismatch** — If you name the Scalekit connection `google-calendar` instead of `googlecalendar`, `get_or_create_connected_account` returns an error. The name in the Dashboard must match the string you pass to `authorize()` exactly. * **Missing OAuth scopes** — If you see a `403 Forbidden` when calling the Calendar or Gmail API, the OAuth app in Google Cloud Console is missing the required scopes. Calendar needs `https://www.googleapis.com/auth/calendar` and Gmail needs `https://www.googleapis.com/auth/gmail.compose`. * **`raise_for_status()` swallowing context** — The default exception message from `requests` truncates the response body. In development, add `print(response.text)` before `raise_for_status()` to see the full error from Google. * **UTC times without timezone info** — Passing a naive `datetime` (without `timezone.utc`) to `isoformat()` produces a string without a `Z` suffix. Google Calendar rejects this with a `400` error. Always construct datetimes with `timezone.utc`. * **`USER_ID` not matching your session** — The script uses a hardcoded `"user_123"`. In production, replace this with the actual user ID from your application’s session. A mismatch means the connected account query returns the wrong user’s tokens. ## Production notes [Section titled “Production notes”](#production-notes) **Timezone handling** — The working-hours check (`WORK_START_HOUR`, `WORK_END_HOUR`) is UTC-only. In production, convert the user’s local timezone and the attendee’s timezone before searching. The `zoneinfo` module (Python 3.9+) handles this without third-party dependencies. **Slot granularity** — The one-hour increment misses 30- and 15-minute openings. For real scheduling, use the busy intervals directly to calculate the gaps between events, then filter by minimum duration. **Multiple calendars** — The `freeBusy` query checks only `primary`. Users who manage work and personal calendars separately will show false availability. Expand the `items` list to include all calendars the user has shared access to. **Draft vs send** — Creating a draft is safer for a first deployment. When you’re confident in the agent’s output quality, switch the Gmail endpoint from `/drafts` to `/messages/send` to make the agent fully autonomous. Add a confirmation step before making this change. **Error recovery** — If `create_event` succeeds but `create_draft` fails, you have an orphaned event with no follow-up email. In production, wrap the two calls in a compensation pattern: track the event ID and delete it if the draft creation fails. **Rate limits** — Google Calendar and Gmail both have per-user quotas. If your agent runs frequently for the same user, add exponential backoff around the `requests.post` calls. ## Next steps [Section titled “Next steps”](#next-steps) * **Add user input** — Replace the hardcoded `ATTENDEE_EMAIL`, `MEETING_TITLE`, and `DURATION_MINUTES` with parameters parsed from natural language using an LLM tool call. * **Build the JavaScript equivalent** — The `agent-auth-examples` repo includes a JavaScript track. Compare the two implementations to see where the patterns converge and where they differ. * **Handle re-authorization** — If a user revokes access, `get_connected_account` returns an inactive account. Add a re-authorization path to recover gracefully instead of crashing. * **Explore other connectors** — The same `authorize()` pattern works for any Scalekit-supported connector: Slack, Notion, Jira. Swap the connector name and replace the Google API calls with the target service’s API. * **Review the Scalekit agent auth quickstart** — For a broader overview of the connected-accounts model, see the [agent auth quickstart](/agentkit/quickstart).

---
# DOCUMENT BOUNDARY
---

# Enforce seat limits with SCIM provisioning

> Block over-quota user creation and alert admins when SCIM pushes users beyond your plan seat limit.

SCIM (System for Cross-domain Identity Management) provisioning runs unsupervised. When a customer’s HR system pushes user #51 to a 50-seat plan, your application will create that user unless you explicitly block it. Scalekit delivers the provisioning events; your application decides whether to act on them. This cookbook shows the two-event pattern that keeps your seat count accurate and tells admins when they need to upgrade their plan. ## SCIM does not enforce seat limits — your app must [Section titled “SCIM does not enforce seat limits — your app must”](#scim-does-not-enforce-seat-limits--your-app-must) Scalekit translates IdP-specific provisioning protocols into a consistent set of webhook events. It does not know your billing model, your seat limits, or which organizations have room for more users. That logic lives in your application. When a user is added in the IdP, Scalekit fires `organization.directory.user_created`. When a user is removed or deactivated, Scalekit fires `organization.directory.user_deleted`. Your webhook handler is the gate between those events and your user table. ## Two webhook events carry the full user lifecycle [Section titled “Two webhook events carry the full user lifecycle”](#two-webhook-events-carry-the-full-user-lifecycle) Both events include the `organization_id`, which lets you look up the seat limit for that specific customer. | Event | When it fires | What to do | | ------------------------------------- | --------------------------------- | ----------------------------------------------------- | | `organization.directory.user_created` | IdP adds or activates a user | Check count — create user or block and notify | | `organization.directory.user_deleted` | IdP removes or deactivates a user | Decrement count — clear any blocked-provisioning flag | ## Track a user count per organization in your database [Section titled “Track a user count per organization in your database”](#track-a-user-count-per-organization-in-your-database) Add a table that stores the provisioned user count and seat limit for each organization. The examples below use plain SQL — translate to your ORM if preferred. db/schema.sql ```sql 1 CREATE TABLE org_seat_usage ( 2 org_id TEXT PRIMARY KEY, 3 seat_limit INTEGER NOT NULL, 4 used_seats INTEGER NOT NULL DEFAULT 0 5 ); ``` Seed this table when you onboard a new customer. Update `seat_limit` whenever the customer upgrades or downgrades their plan. ## Block creation when the count reaches the limit [Section titled “Block creation when the count reaches the limit”](#block-creation-when-the-count-reaches-the-limit) The `user_created` handler increments the seat counter and creates the user only when there is room. Always return `200` to Scalekit — returning an error code causes Scalekit to retry delivery, which does not help when the block is intentional. Verify webhook signatures before processing Always verify that events come from Scalekit before acting on them. An unverified endpoint that mutates your database can be triggered by forged requests. See the [SCIM provisioning quickstart](/directory/scim/quickstart/) for how to verify signatures using the Scalekit SDK. * Node.js webhook-handler.ts ```ts 1 import express from 'express' 2 3 const app = express() 4 app.use(express.json()) 5 6 app.post('/webhooks/scalekit', async (req, res) => { 7 const event = req.body 8 9 if (event.type === 'organization.directory.user_created') { 10 const orgId = event.organization_id 11 const directoryUser = event.data 12 let seatLimitReached = false 13 14 // Run the check and insert in a single transaction. 15 // FOR UPDATE inside the transaction holds the lock until commit. 16 await db.transaction(async (tx) => { 17 const usage = await tx.queryOne( 18 'SELECT seat_limit, used_seats FROM org_seat_usage WHERE org_id = $1 FOR UPDATE', 19 [orgId] 20 ) 21 22 if (!usage || usage.used_seats >= usage.seat_limit) { 23 seatLimitReached = true 24 return 25 } 26 27 await tx.query( 28 'INSERT INTO users (id, org_id, email, name) VALUES ($1, $2, $3, $4)', 29 [directoryUser.id, orgId, directoryUser.email, directoryUser.name] 30 ) 31 await tx.query( 32 'UPDATE org_seat_usage SET used_seats = used_seats + 1 WHERE org_id = $1', 33 [orgId] 34 ) 35 }) 36 37 if (seatLimitReached) { 38 // Seat limit reached — skip user creation and alert the admin. 39 await notifyAdminSeatLimitReached(orgId) 40 } 41 } 42 43 // Return 200 so Scalekit does not retry this event. 44 res.sendStatus(200) 45 }) ``` * Python webhook\_handler.py ```python 1 from flask import Flask, request 2 3 app = Flask(__name__) 4 5 @app.route('/webhooks/scalekit', methods=['POST']) 6 def handle_webhook(): 7 event = request.get_json() 8 9 if event.get('type') == 'organization.directory.user_created': 10 org_id = event['organization_id'] 11 directory_user = event['data'] 12 seat_limit_reached = False 13 14 # Run the check and insert in a single transaction. 15 # FOR UPDATE inside the transaction holds the lock until commit. 16 with db.transaction() as tx: 17 usage = tx.query_one( 18 'SELECT seat_limit, used_seats FROM org_seat_usage ' 19 'WHERE org_id = %s FOR UPDATE', 20 (org_id,) 21 ) 22 23 if not usage or usage['used_seats'] >= usage['seat_limit']: 24 seat_limit_reached = True 25 else: 26 tx.execute( 27 'INSERT INTO users (id, org_id, email, name) VALUES (%s, %s, %s, %s)', 28 (directory_user['id'], org_id, 29 directory_user['email'], directory_user['name']) 30 ) 31 tx.execute( 32 'UPDATE org_seat_usage SET used_seats = used_seats + 1 ' 33 'WHERE org_id = %s', 34 (org_id,) 35 ) 36 37 if seat_limit_reached: 38 # Seat limit reached — skip user creation and alert the admin. 39 notify_admin_seat_limit_reached(org_id) 40 41 # Return 200 so Scalekit does not retry this event. 42 return '', 200 ``` * Go webhook\_handler.go ```go 1 package main 2 3 import ( 4 "encoding/json" 5 "net/http" 6 ) 7 8 func webhookHandler(w http.ResponseWriter, r *http.Request) { 9 var event map[string]interface{} 10 if err := json.NewDecoder(r.Body).Decode(&event); err != nil { 11 http.Error(w, "bad request", http.StatusBadRequest) 12 return 13 } 14 15 if event["type"] == "organization.directory.user_created" { 16 orgID := event["organization_id"].(string) 17 data := event["data"].(map[string]interface{}) 18 seatLimitReached := false 19 20 // Run the check and insert in a single transaction. 21 // FOR UPDATE inside the transaction holds the lock until commit. 22 tx, _ := db.Begin() 23 var seatLimit, usedSeats int 24 err := tx.QueryRow( 25 "SELECT seat_limit, used_seats FROM org_seat_usage WHERE org_id = $1 FOR UPDATE", 26 orgID, 27 ).Scan(&seatLimit, &usedSeats) 28 29 if err != nil || usedSeats >= seatLimit { 30 seatLimitReached = true 31 tx.Rollback() 32 } else { 33 tx.Exec( 34 "INSERT INTO users (id, org_id, email, name) VALUES ($1, $2, $3, $4)", 35 data["id"], orgID, data["email"], data["name"], 36 ) 37 tx.Exec( 38 "UPDATE org_seat_usage SET used_seats = used_seats + 1 WHERE org_id = $1", 39 orgID, 40 ) 41 tx.Commit() 42 } 43 44 if seatLimitReached { 45 // Seat limit reached — skip user creation and alert the admin. 46 notifyAdminSeatLimitReached(orgID) 47 } 48 } 49 50 // Return 200 so Scalekit does not retry this event. 51 w.WriteHeader(http.StatusOK) 52 } ``` * Java WebhookController.java ```java 1 import org.springframework.web.bind.annotation.*; 2 import java.util.Map; 3 import java.util.concurrent.atomic.AtomicBoolean; 4 5 @RestController 6 public class WebhookController { 7 8 @PostMapping("/webhooks/scalekit") 9 public ResponseEntity handleWebhook(@RequestBody Map event) { 10 if ("organization.directory.user_created".equals(event.get("type"))) { 11 String orgId = (String) event.get("organization_id"); 12 Map directoryUser = (Map) event.get("data"); 13 AtomicBoolean seatLimitReached = new AtomicBoolean(false); 14 15 // Run the check and insert in a single transaction. 16 // FOR UPDATE inside the transaction holds the lock until commit. 17 transactionTemplate.execute(status -> { 18 OrgSeatUsage usage = db.queryForObject( 19 "SELECT seat_limit, used_seats FROM org_seat_usage WHERE org_id = ? FOR UPDATE", 20 OrgSeatUsage.class, orgId 21 ); 22 23 if (usage == null || usage.getUsedSeats() >= usage.getSeatLimit()) { 24 seatLimitReached.set(true); 25 return null; 26 } 27 28 db.update( 29 "INSERT INTO users (id, org_id, email, name) VALUES (?, ?, ?, ?)", 30 directoryUser.get("id"), orgId, 31 directoryUser.get("email"), directoryUser.get("name") 32 ); 33 db.update( 34 "UPDATE org_seat_usage SET used_seats = used_seats + 1 WHERE org_id = ?", 35 orgId 36 ); 37 return null; 38 }); 39 40 if (seatLimitReached.get()) { 41 // Seat limit reached — skip user creation and alert the admin. 42 notifyAdminSeatLimitReached(orgId); 43 } 44 } 45 46 // Return 200 so Scalekit does not retry this event. 47 return ResponseEntity.ok().build(); 48 } 49 } ``` ## Decrement the count when a user is removed [Section titled “Decrement the count when a user is removed”](#decrement-the-count-when-a-user-is-removed) The `user_deleted` handler decreases the seat counter and clears any pending seat-limit notification. This lets the next `user_created` event succeed without manual intervention from your team. * Node.js webhook-handler.ts ```ts 1 if (event.type === 'organization.directory.user_deleted') { 2 const orgId = event.organization_id 3 const directoryUser = event.data 4 5 await db.transaction(async (tx) => { 6 // Remove the user and decrement the counter atomically. 7 await tx.query('DELETE FROM users WHERE id = $1', [directoryUser.id]) 8 await tx.query( 9 'UPDATE org_seat_usage SET used_seats = GREATEST(used_seats - 1, 0) WHERE org_id = $1', 10 [orgId] 11 ) 12 // Clear any pending seat-limit notification so the next user can be provisioned. 13 await tx.query( 14 "DELETE FROM notifications WHERE org_id = $1 AND type = 'seat_limit_reached'", 15 [orgId] 16 ) 17 }) 18 } ``` * Python webhook\_handler.py ```python 1 if event.get('type') == 'organization.directory.user_deleted': 2 org_id = event['organization_id'] 3 directory_user = event['data'] 4 5 with db.transaction() as tx: 6 # Remove the user and decrement the counter atomically. 7 tx.execute('DELETE FROM users WHERE id = %s', (directory_user['id'],)) 8 tx.execute( 9 'UPDATE org_seat_usage SET used_seats = GREATEST(used_seats - 1, 0) ' 10 'WHERE org_id = %s', 11 (org_id,) 12 ) 13 # Clear any pending seat-limit notification so the next user can be provisioned. 14 tx.execute( 15 "DELETE FROM notifications WHERE org_id = %s AND type = 'seat_limit_reached'", 16 (org_id,) 17 ) ``` * Go webhook\_handler.go ```go 1 if event["type"] == "organization.directory.user_deleted" { 2 orgID := event["organization_id"].(string) 3 data := event["data"].(map[string]interface{}) 4 5 tx, _ := db.Begin() 6 // Remove the user and decrement the counter atomically. 7 tx.Exec("DELETE FROM users WHERE id = $1", data["id"]) 8 tx.Exec( 9 "UPDATE org_seat_usage SET used_seats = GREATEST(used_seats - 1, 0) WHERE org_id = $1", 10 orgID, 11 ) 12 // Clear any pending seat-limit notification so the next user can be provisioned. 13 tx.Exec( 14 "DELETE FROM notifications WHERE org_id = $1 AND type = 'seat_limit_reached'", 15 orgID, 16 ) 17 tx.Commit() 18 } ``` * Java WebhookController.java ```java 1 if ("organization.directory.user_deleted".equals(event.get("type"))) { 2 String orgId = (String) event.get("organization_id"); 3 Map directoryUser = (Map) event.get("data"); 4 5 transactionTemplate.execute(status -> { 6 // Remove the user and decrement the counter atomically. 7 db.update("DELETE FROM users WHERE id = ?", directoryUser.get("id")); 8 db.update( 9 "UPDATE org_seat_usage SET used_seats = GREATEST(used_seats - 1, 0) WHERE org_id = ?", 10 orgId 11 ); 12 // Clear any pending seat-limit notification so the next user can be provisioned. 13 db.update( 14 "DELETE FROM notifications WHERE org_id = ? AND type = 'seat_limit_reached'", 15 orgId 16 ); 17 return null; 18 }); 19 } ``` ## Notify admins without spamming them [Section titled “Notify admins without spamming them”](#notify-admins-without-spamming-them) A new `user_created` event fires for every blocked user. Without deduplication, your admin will receive one email per rejected provisioning attempt. Use an idempotent insert to fire the notification only once per organization until the condition is resolved. db/schema.sql ```sql 1 CREATE TABLE notifications ( 2 id SERIAL PRIMARY KEY, 3 org_id TEXT NOT NULL, 4 type TEXT NOT NULL, 5 resolved BOOLEAN NOT NULL DEFAULT FALSE, 6 created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), 7 UNIQUE (org_id, type, resolved) 8 ); ``` The `UNIQUE (org_id, type, resolved)` constraint blocks duplicate active notifications. Insert with `ON CONFLICT DO NOTHING` to skip the insert when a notification already exists: * Node.js notify.ts ```ts 1 async function notifyAdminSeatLimitReached(orgId: string) { 2 // Insert only if no unresolved notification exists for this org. 3 const result = await db.query( 4 `INSERT INTO notifications (org_id, type, resolved) 5 VALUES ($1, 'seat_limit_reached', FALSE) 6 ON CONFLICT (org_id, type, resolved) DO NOTHING`, 7 [orgId] 8 ) 9 10 // rowCount is 0 when the conflict was skipped — admin already notified. 11 if (result.rowCount === 0) return 12 13 // Send the alert once: email, Slack, in-app — your choice. 14 await sendAdminAlert(orgId, 'Seat limit reached — users are not being provisioned.') 15 } ``` * Python notify.py ```python 1 def notify_admin_seat_limit_reached(org_id: str) -> None: 2 # Insert only if no unresolved notification exists for this org. 3 result = db.execute( 4 """INSERT INTO notifications (org_id, type, resolved) 5 VALUES (%s, 'seat_limit_reached', FALSE) 6 ON CONFLICT (org_id, type, resolved) DO NOTHING""", 7 (org_id,) 8 ) 9 10 # rowcount is 0 when the conflict was skipped — admin already notified. 11 if result.rowcount == 0: 12 return 13 14 # Send the alert once: email, Slack, in-app — your choice. 15 send_admin_alert(org_id, 'Seat limit reached — users are not being provisioned.') ``` * Go notify.go ```go 1 func notifyAdminSeatLimitReached(orgID string) { 2 // Insert only if no unresolved notification exists for this org. 3 result, _ := db.Exec( 4 `INSERT INTO notifications (org_id, type, resolved) 5 VALUES ($1, 'seat_limit_reached', FALSE) 6 ON CONFLICT (org_id, type, resolved) DO NOTHING`, 7 orgID, 8 ) 9 10 // RowsAffected is 0 when the conflict was skipped — admin already notified. 11 rows, _ := result.RowsAffected() 12 if rows == 0 { 13 return 14 } 15 16 // Send the alert once: email, Slack, in-app — your choice. 17 sendAdminAlert(orgID, "Seat limit reached — users are not being provisioned.") 18 } ``` * Java NotificationService.java ```java 1 public void notifyAdminSeatLimitReached(String orgId) { 2 // Insert only if no unresolved notification exists for this org. 3 int rows = db.update( 4 "INSERT INTO notifications (org_id, type, resolved) " + 5 "VALUES (?, 'seat_limit_reached', FALSE) " + 6 "ON CONFLICT (org_id, type, resolved) DO NOTHING", 7 orgId 8 ); 9 10 // rows is 0 when the conflict was skipped — admin already notified. 11 if (rows == 0) return; 12 13 // Send the alert once: email, Slack, in-app — your choice. 14 sendAdminAlert(orgId, "Seat limit reached — users are not being provisioned."); 15 } ``` When a user is removed and the count drops below the limit, the `user_deleted` handler deletes the notification row. The next blocked `user_created` event will insert a fresh notification and trigger a new alert. *** **Related guides** * [SCIM provisioning quickstart](/directory/scim/quickstart/) — set up webhooks and the Directory API, including signature verification * [Directory webhook events reference](/reference/webhooks/directory-events/) — full event payload schemas

---
# DOCUMENT BOUNDARY
---

# Search Scalekit docs with ref.tools

> Configure ref.tools MCP to search Scalekit documentation directly from Cursor, Claude Code, or Windsurf without leaving your IDE.

Every time you need to look up a Scalekit API, scope name, or configuration option, you break your flow: open a new tab, search the docs, copy the answer, switch back. With ref.tools configured as an MCP server, your AI coding assistant can search Scalekit documentation inline and return accurate, up-to-date answers without you leaving the editor. Setup takes about two minutes. ## The problem [Section titled “The problem”](#the-problem) AI coding assistants are good at generating code, but they have two failure modes when it comes to third-party docs: * **Hallucination** — The model invents an API that doesn’t exist or gets parameter names wrong because its training data is incomplete * **Stale knowledge** — Even accurate training data goes out of date as SDKs and APIs evolve Both problems get worse when you’re working with a narrowly scoped platform like Scalekit. The model may have seen very little training data about it, and what it did see may be outdated. The standard workaround is to paste docs into the chat manually — which means constant context-switching between your editor and a browser. ref.tools solves both problems by connecting your AI assistant directly to live Scalekit documentation through an MCP tool call. ## Who needs this [Section titled “Who needs this”](#who-needs-this) This cookbook is for you if: * ✅ You use Cursor, Claude Code, Windsurf, or another MCP-compatible AI assistant * ✅ You’re building with Scalekit (auth, SSO, MCP servers, M2M, SCIM) * ✅ You want accurate, up-to-date answers without context-switching to a browser You **don’t** need this if: * ❌ You prefer pasting docs into your chat manually * ❌ Your AI assistant doesn’t support MCP ## The solution [Section titled “The solution”](#the-solution) [ref.tools](https://ref.tools) is a documentation search platform that indexes third-party docs — including Scalekit — and exposes them as an MCP tool called `ref_search_documentation`. Once you add the ref.tools MCP server to your AI assistant, you can prompt it to search Scalekit docs and it will call the tool and return current results directly in chat. The server supports two transports: * **Streamable HTTP** (recommended) — Direct HTTP connection using your API key; lower latency, no local process required * **stdio** (legacy) — Runs a local `npx` process; works with any MCP client that supports stdio ## Set up ref.tools [Section titled “Set up ref.tools”](#set-up-reftools) 1. ### Get your API key [Section titled “Get your API key”](#get-your-api-key) 1. Go to [ref.tools](https://ref.tools) and sign in 2. Search for **Scalekit** to confirm the documentation source is indexed 3. Open the **Quick Install** panel for Scalekit — your API key is pre-filled in the install commands 4. Copy your API key; you’ll use it in the next step 2. ### Add the MCP server to your AI assistant [Section titled “Add the MCP server to your AI assistant”](#add-the-mcp-server-to-your-ai-assistant) Pick your tool and apply the matching configuration. #### Claude Code [Section titled “Claude Code”](#claude-code) Run this command in your terminal to add the MCP server globally across all projects: ```bash 1 claude mcp add --transport http ref-context https://api.ref.tools/mcp \ 2 --header "x-ref-api-key: YOUR_API_KEY" ``` To scope it to a single project instead, add `--scope project` to the command. #### Cursor [Section titled “Cursor”](#cursor) Add the following to `.cursor/mcp.json` in your project root (or via **Settings → MCP**): .cursor/mcp.json ```json 1 { 2 "ref-context": { 3 "type": "http", 4 "url": "https://api.ref.tools/mcp?apiKey=YOUR_API_KEY" 5 } 6 } ``` #### Windsurf [Section titled “Windsurf”](#windsurf) Add the following to `~/.codeium/windsurf/mcp_config.json`: \~/.codeium/windsurf/mcp\_config.json ```json 1 { 2 "ref-context": { 3 "serverUrl": "https://api.ref.tools/mcp?apiKey=YOUR_API_KEY" 4 } 5 } ``` #### Other (stdio) [Section titled “Other (stdio)”](#other-stdio) For any MCP client that supports stdio, add to your MCP config: mcp.json ```json 1 { 2 "ref-context": { 3 "command": "npx", 4 "args": ["ref-tools-mcp@latest"], 5 "env": { 6 "REF_API_KEY": "YOUR_API_KEY" 7 } 8 } 9 } ``` This requires Node.js installed locally. The `npx` command fetches and runs the server on first use. 3. ### Verify it’s working [Section titled “Verify it’s working”](#verify-its-working) 1. Restart your AI assistant (or use its MCP reload command if available) 2. Open a new chat and send this prompt: ```plaintext 1 Use ref to look up how to add OAuth 2.1 authorization to an MCP server with Scalekit ``` 3. Your assistant should call the `ref_search_documentation` tool and return results from `docs.scalekit.com` If the tool doesn’t appear, check that you restarted the assistant after saving the config, and that the API key is correct. Keep your API key private Never commit your ref.tools API key to source control. For project-level configs checked into git, pass the key through an environment variable and reference it as `$REF_API_KEY` in your config, or add the config file to `.gitignore`. ## Example searches to try [Section titled “Example searches to try”](#example-searches-to-try) Once ref.tools is connected, use phrases like “use ref to…” or “look up in ref…” to trigger the tool explicitly: * `Use ref to find the Scalekit MCP auth quickstart` * `Look up how to configure SSO with Scalekit` * `Use ref to find Scalekit M2M token documentation` * `Search Scalekit docs for SCIM provisioning setup` * `Use ref to look up Scalekit SDK environment variables` You can also just ask naturally — most assistants will call the tool automatically when the question is about Scalekit. ## Common mistakes [Section titled “Common mistakes”](#common-mistakes) ## Next steps [Section titled “Next steps”](#next-steps) For further setup, authentication options, and available documentation sources, see the links below. * [Add OAuth 2.1 authorization to MCP servers](/authenticate/mcp/quickstart) — the most common thing developers look up using ref * [ref.tools](https://ref.tools) — browse all available documentation sources you can add alongside Scalekit * [M2M authentication overview](/guides/m2m/overview) — machine-to-machine auth patterns frequently searched via ref

---
# DOCUMENT BOUNDARY
---

# Set up AgentKit with your coding agent

> Add Scalekit Agent Auth to your codebase using Claude Code, Codex, GitHub Copilot CLI, Cursor, or any of 40+ coding agents.

Install the Scalekit Auth Stack plugin into your coding agent and paste one prompt. The agent generates client initialization, connected account management, OAuth authorization, and token handling — no boilerplate required. ## Before you start [Section titled “Before you start”](#before-you-start) * A Scalekit account at [app.scalekit.com](https://app.scalekit.com) * A connector configured under **AgentKit** > **Connections** (for example, `gmail`) * Your API credentials from **Developers → API Credentials** ## Pick your coding agent [Section titled “Pick your coding agent”](#pick-your-coding-agent) * Claude Code Terminal ```bash claude plugin marketplace add scalekit-inc/claude-code-authstack && claude plugin install agent-auth@scalekit-auth-stack ``` Installing the plugin sets up Scalekit’s MCP server and triggers an OAuth authorization flow in your browser. Complete the authorization before continuing — this gives Claude Code direct access to your Scalekit environment to search docs, manage connections, and check connected account status. Then paste this prompt: Implementation prompt ```md Configure Scalekit agent authentication for [connector-name]. Provide code to create a connected account, generate an authorization link, retrieve the token, and call the API on behalf of the user. ``` * Codex Terminal ```bash curl -fsSL https://raw.githubusercontent.com/scalekit-inc/codex-authstack/main/install.sh | bash ``` Restart Codex → Plugin Directory → **Scalekit Auth Stack** → install **agent-auth**. If a browser authorization prompt appears, complete the OAuth flow before continuing. Then paste the implementation prompt above. * GitHub Copilot CLI Terminal ```bash copilot plugin marketplace add scalekit-inc/github-copilot-authstack copilot plugin install agent-auth@scalekit-auth-stack ``` Then run: Terminal ```bash copilot "Configure Scalekit agent authentication for [connector-name]. Provide code to create a connected account, generate an authorization link, retrieve the token, and call the API on behalf of the user." ``` * Cursor Terminal ```bash curl -fsSL https://raw.githubusercontent.com/scalekit-inc/cursor-authstack/main/install.sh | bash ``` Reload Cursor → **Settings → Plugins** → enable **Agent Auth**. If a browser authorization prompt appears, complete the OAuth flow before continuing. Open chat (Cmd+L / Ctrl+L) and paste the implementation prompt above. * 40+ agents Terminal ```bash npx skills add scalekit-inc/skills --skill integrating-agent-auth ``` Then ask your agent to configure Scalekit authentication for your connector and generate connected account, auth link, and token-fetch code. Supported agents include Claude Code, Cursor, GitHub Copilot CLI, OpenCode, Windsurf, Cline, Gemini CLI, Codex, and 30+ others. ## Verify the setup [Section titled “Verify the setup”](#verify-the-setup) 1. **Set environment variables** — copy `SCALEKIT_CLIENT_ID`, `SCALEKIT_CLIENT_SECRET`, and `SCALEKIT_ENV_URL` from the dashboard → **API Credentials**. 2. **Trigger the authorization flow** — run the generated example and confirm the browser redirects to the connector’s consent page. 3. **Fetch a token** — after consent, call the token-fetch function and confirm you receive a valid response. Review generated code before deploying Verify that token validation logic, error handling, and environment variable references match your application’s requirements. The generated code is a foundation, not a finished implementation. ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting)

---
# DOCUMENT BOUNDARY
---

# Bring your own credentials

> Configure your own OAuth app credentials so users see your brand on consent screens, not Scalekit's.

By default, Scalekit uses its own OAuth app credentials when your users go through the OAuth consent flow. This works for development and testing, but in production your users will see Scalekit’s name and branding on the consent screen, not yours. **Bring your own credentials** lets you replace Scalekit’s shared OAuth credentials with your own. Once configured, users see your app name, logo, and terms on every OAuth consent screen. ## What changes when you use your own credentials [Section titled “What changes when you use your own credentials”](#what-changes-when-you-use-your-own-credentials) * **Consent screens** display your application’s name and branding * **Rate limits and quotas** are tied to your OAuth app, not Scalekit’s shared pool * **Provider relationship** is direct, and your OAuth app appears in provider dashboards and audit logs * **Compliance**: useful if your organization requires a direct relationship with each OAuth provider Nothing changes in your code or the Scalekit SDK. The switch is purely a dashboard configuration on the connection. ## Configure your credentials [Section titled “Configure your credentials”](#configure-your-credentials) 1. ### Copy the redirect URI from Scalekit [Section titled “Copy the redirect URI from Scalekit”](#copy-the-redirect-uri-from-scalekit) Go to **AgentKit** > **Connections** and click **Edit** on the connection you want to update. Select **Use your own credentials**. The form expands and displays a **Redirect URI**. Copy it. 2. ### Register your OAuth app with the provider [Section titled “Register your OAuth app with the provider”](#register-your-oauth-app-with-the-provider) In the provider’s developer console, create a new OAuth app (or use an existing one). Add the Redirect URI you copied in the previous step to the list of authorized redirect URIs. Redirect URI must match exactly The URI must match character-for-character. A mismatch will cause OAuth flows to fail with a redirect\_uri\_mismatch error. The provider gives you a **Client ID** and **Client Secret** after registration. 3. ### Enter your credentials and save [Section titled “Enter your credentials and save”](#enter-your-credentials-and-save) Back in Scalekit Dashboard, enter the **Client ID** and **Client Secret** from your OAuth app and click **Save**. All new OAuth flows for this connection will now use your credentials. ## Existing connected accounts [Section titled “Existing connected accounts”](#existing-connected-accounts) Existing connected accounts are not affected immediately Switching credentials does not re-authorize users who are already active. They continue using the previous credentials until they re-authorize. If you need all users to see your branding immediately, generate new authorization links and prompt them to re-authorize.

---
# DOCUMENT BOUNDARY
---

# Set up a custom domain

> Replace the default Scalekit endpoint with your own branded domain using CNAME configuration.

Custom domains enable you to offer a fully branded experience. By default, Scalekit assigns a unique endpoint URL, but you can replace it via CNAME configuration. The custom domain also applies to the authorization server URL shown on the OAuth consent screen during MCP authentication; users will see your branded domain instead of the auto-generated `yourapp-xxxx.scalekit.com`. | Before | After | | ------------------------------ | -------------------------- | | `https://yourapp.scalekit.com` | `https://auth.yourapp.com` | * **Environment:** CNAME configuration is available only for production environments * **SSL:** After successful CNAME configuration, an SSL certificate for your custom domain is automatically provisioned ## Set up your custom domain [Section titled “Set up your custom domain”](#set-up-your-custom-domain) ![](/.netlify/images?url=_astro%2F1.BktW9U-H.png\&w=2786\&h=1746\&dpl=6a01bf5aba8408000850fe26) To set up your custom domain: 1. Go to your domain’s DNS registrar 2. Add a new record to your DNS settings and select **CNAME** as the record type 3. Switch to production environment in the Scalekit dashboard 4. Copy the **Name** (your desired subdomain) from the Scalekit dashboard > Settings > Custom domains and paste it into the **Name/Label/Host** field in your DNS registrar 5. Copy the **Value** from the Scalekit dashboard > Settings > Custom domains and paste it into the **Destination/Target/Value** field in your DNS registrar 6. Save the record in your DNS registrar 7. In the Scalekit dashboard, click **Verify** CNAME record changes can take up to 72 hours to propagate, although they typically happen much sooner. ## Troubleshoot CNAME verification [Section titled “Troubleshoot CNAME verification”](#troubleshoot-cname-verification) If there are any issues during the CNAME verification step: * Double-check your DNS configuration to ensure all values are correctly entered * Once the CNAME changes take effect, Scalekit will automatically provision an SSL certificate for your custom domain. This process can take up to 24 hours You can click on the **Check** button in the Scalekit dashboard to verify SSL certification status. If SSL provisioning takes longer than 24 hours, please contact us at [](mailto:support@scalekit.com) ## DNS registrar guides [Section titled “DNS registrar guides”](#dns-registrar-guides) For detailed instructions on adding a CNAME record in specific registrars: * [GoDaddy: Add a CNAME record](https://www.godaddy.com/en-in/help/add-a-cname-record-19236) * [Namecheap: How to create a CNAME record](https://www.namecheap.com/support/knowledgebase/article.aspx/9646/2237/how-to-create-a-cname-record-for-your-domain)

---
# DOCUMENT BOUNDARY
---

# AgentKit launch checklist

> Verify your AgentKit integration is production-ready before going live.

Use this checklist before moving your AgentKit integration to production. ## Environment and credentials [Section titled “Environment and credentials”](#environment-and-credentials) * \[ ] Switch to the production environment in the Scalekit dashboard * \[ ] Set `SCALEKIT_ENV_URL`, `SCALEKIT_CLIENT_ID`, and `SCALEKIT_CLIENT_SECRET` to production values, not dev or staging ## Connections [Section titled “Connections”](#connections) * \[ ] All connectors your agent uses are configured in the production environment * \[ ] Each connection shows as active in the dashboard * \[ ] Connection names used in code match the names in the dashboard exactly ## Authorization and connected accounts [Section titled “Authorization and connected accounts”](#authorization-and-connected-accounts) * \[ ] End-to-end authorization flow tested with a real user account in production * \[ ] Connected accounts created and verified for at least one test user * \[ ] Magic link generation and redirect tested (OAuth connectors) * \[ ] Re-authorization flow tested: verify behavior when a token expires or is revoked ## Security [Section titled “Security”](#security) * \[ ] MCP URLs are generated and consumed server-side only; never passed to or generated in client-side code * \[ ] `identifier` values passed to Tool Proxy are tied to authenticated users, not shared, static, or guessable * \[ ] Per-user MCP URLs are not cached longer than the session they were issued for ## Custom connector (if applicable) [Section titled “Custom connector (if applicable)”](#custom-connector-if-applicable) * \[ ] Connector definition promoted from Dev to Production (see [Create your own connector](/agentkit/bring-your-own-connector/create-connector)) * \[ ] Auth pattern validated with a real connected account in production * \[ ] Tool Proxy calls return expected responses against the production upstream API ## Go live [Section titled “Go live”](#go-live) * \[ ] Custom domain configured and SSL verified (see [Custom domain](/agentkit/advanced/custom-domain))

---
# DOCUMENT BOUNDARY
---

# Proxy API Calls

> Use Scalekit managed authentication and make direct HTTP calls to third party applications

Even though Scalekit Agent Auth offers pre-built connector tools out of the box for the supported applications, if you would like to make direct API calls to the third party applications for any custom behaviour, you can leverage proxy\_api tool to directly invoke the third party application. Based on the connected account or user identifier details, Scalekit will automatically inject the user authorization tokens so that API calls to the third application will be successful. Proxy must be enabled per environment Proxy access for built-in providers (Gmail, Notion, Slack, and others) is **not enabled by default** on new environments. If you receive the error `proxy not enabled for provider`, contact  to enable the proxy for your environment. ```python 1 # Fetch recent emails 2 emails = actions.tools.execute( 3 connected_account_id=connected_account.id, 4 tool='gmail_proxy_api', 5 parameters={ 6 'path': '/gmail/v1/users/me/messages', 7 'method': 'GET', 8 'headers': [{'Content-Type': 'application/json'}], 9 'params': [{'max_results': '5'}], 10 'body': '' #actual JSON payload 11 } 12 ) 13 14 print(f'Recent emails: {emails.result}') ``` As part of the above execution, Scalekit will automatically inject Bearer token in the request header before making the API call to GMAIL.

---
# DOCUMENT BOUNDARY
---

# Authentication Methods Comparison

> Compare different authentication methods supported by AgentKit including OAuth 2.0, API Keys, Bearer Tokens, and Custom JWT to choose the right approach.

AgentKit supports multiple authentication methods to connect with third-party providers. This guide helps you understand the differences and choose the right authentication method for your use case. ## Authentication methods overview [Section titled “Authentication methods overview”](#authentication-methods-overview) OAuth 2.0 **Most secure and widely supported** User-delegated authentication with automatic token refresh and granular permissions. **Best for:** Google, Microsoft, Slack, GitHub API Keys **Simple static credentials** Provider-issued keys for straightforward server-to-server authentication. **Best for:** Jira, Asana, Linear, Airtable Bearer Tokens **User-generated tokens** Personal access tokens with scoped permissions for individual use. **Best for:** GitHub PATs, GitLab tokens Custom JWT **Advanced signed tokens** Cryptographically signed tokens for service accounts and custom protocols. **Best for:** Custom integrations, service accounts ## Comparison matrix [Section titled “Comparison matrix”](#comparison-matrix) | Feature | OAuth 2.0 | API Keys | Bearer Tokens | Custom JWT | | ------------------------ | ---------- | -------- | ------------- | ------------ | | **Security Level** | High | Medium | Medium | High | | **User Interaction** | Required | Optional | Required | Not required | | **Token Refresh** | Automatic | N/A | Manual | Varies | | **Setup Complexity** | Moderate | Easy | Easy | Complex | | **Granular Permissions** | Yes | Limited | Yes | Limited | | **Provider Support** | Widespread | Common | Common | Limited | ## When to use each method [Section titled “When to use each method”](#when-to-use-each-method) ### OAuth 2.0 [Section titled “OAuth 2.0”](#oauth-20) **Use when:** * Provider supports OAuth * Acting on behalf of users * Need automatic token refresh * Require granular permissions * Building user-facing applications **Example:** User connects Gmail to send emails through your app ### API Keys [Section titled “API Keys”](#api-keys) **Use when:** * Provider only supports API keys * Building internal tools * Server-to-server communication * Simplicity is priority **Example:** Automated Jira ticket creation for support system ### Bearer Tokens [Section titled “Bearer Tokens”](#bearer-tokens) **Use when:** * Personal access is sufficient * Building developer tools * OAuth unavailable * User prefers manual control **Example:** Personal GitHub repository automation ### Custom JWT [Section titled “Custom JWT”](#custom-jwt) **Use when:** * Provider requires JWT * Service account access needed * Custom authentication protocol * Advanced security requirements **Example:** Enterprise service account integrations ## Next steps [Section titled “Next steps”](#next-steps) * [Connectors](/agentkit/connectors) - Available third-party providers * [Connections](/agentkit/connections) - Configure provider connections * [Authorization Methods](/agentkit/tools/authorize) - Detailed authentication implementation

---
# DOCUMENT BOUNDARY
---

# Multi-Provider Authentication

> Learn how to manage authentication for multiple third-party providers simultaneously, handle different auth states, and provide seamless user experiences.

When building applications with Agent Auth, users often need to connect multiple third-party providers. This guide shows you how to manage multiple authenticated connections per user effectively. ## Understanding multi-provider scenarios [Section titled “Understanding multi-provider scenarios”](#understanding-multi-provider-scenarios) Users might connect multiple providers for different purposes: * **Email & Calendar**: Gmail + Google Calendar + Slack * **Project Management**: Jira + GitHub + Slack notifications * **Productivity Suite**: Microsoft 365 + Notion + Asana * **Support Systems**: Gmail + Slack + Jira + Salesforce ## Managing multiple connected accounts [Section titled “Managing multiple connected accounts”](#managing-multiple-connected-accounts) ### Create connections for multiple providers [Section titled “Create connections for multiple providers”](#create-connections-for-multiple-providers) Each provider requires a separate connected account: * Python ```python 1 # Create connected accounts for multiple providers 2 providers = ["gmail", "slack", "jira"] 3 user_id = "user_123" 4 5 for provider in providers: 6 response = actions.get_or_create_connected_account( 7 connection_name=provider, 8 identifier=user_id 9 ) 10 11 account = response.connected_account 12 print(f"{provider}: {account.status}") 13 14 # Generate authorization link if not active 15 if account.status != "ACTIVE": 16 link = actions.get_authorization_link( 17 connection_name=provider, 18 identifier=user_id 19 ) 20 print(f" Authorize {provider}: {link.link}") ``` * Node.js ```typescript 1 import { ConnectorStatus } from '@scalekit-sdk/node/lib/pkg/grpc/scalekit/v1/connected_accounts/connected_accounts_pb'; 2 3 // Create connected accounts for multiple providers 4 const providers = ['gmail', 'slack', 'jira']; 5 const userId = 'user_123'; 6 7 for (const provider of providers) { 8 const response = await scalekit.actions.getOrCreateConnectedAccount({ 9 connectionName: provider, 10 identifier: userId 11 }); 12 13 const account = response.connectedAccount; 14 console.log(`${provider}: ${account.status}`); 15 16 // Generate authorization link if not active 17 if (account.status !== ConnectorStatus.ACTIVE) { 18 const link = await scalekit.actions.getAuthorizationLink({ 19 connectionName: provider, 20 identifier: userId 21 }); 22 console.log(` Authorize ${provider}: ${link.link}`); 23 } 24 } ``` * Go ```go 1 // Create connected accounts for multiple providers 2 providers := []string{"gmail", "slack", "jira"} 3 userID := "user_123" 4 5 for _, provider := range providers { 6 response, err := scalekitClient.Actions.GetOrCreateConnectedAccount( 7 context.Background(), 8 provider, 9 userID, 10 ) 11 if err != nil { 12 log.Printf("Error for %s: %v", provider, err) 13 continue 14 } 15 16 account := response.ConnectedAccount 17 fmt.Printf("%s: %s\n", provider, account.Status) 18 19 // Generate authorization link if not active 20 if account.Status != "ACTIVE" { 21 link, _ := scalekitClient.Actions.GetAuthorizationLink( 22 context.Background(), 23 provider, 24 userID, 25 ) 26 fmt.Printf(" Authorize %s: %s\n", provider, link.Link) 27 } 28 } ``` * Java ```java 1 // Create connected accounts for multiple providers 2 String[] providers = {"gmail", "slack", "jira"}; 3 String userId = "user_123"; 4 5 for (String provider : providers) { 6 ConnectedAccountResponse response = scalekitClient.actions() 7 .getOrCreateConnectedAccount(provider, userId); 8 9 ConnectedAccount account = response.getConnectedAccount(); 10 System.out.println(provider + ": " + account.getStatus()); 11 12 // Generate authorization link if not active 13 if (!"ACTIVE".equals(account.getStatus())) { 14 AuthorizationLink link = scalekitClient.actions() 15 .getAuthorizationLink(provider, userId); 16 System.out.println(" Authorize " + provider + ": " + link.getLink()); 17 } 18 } ``` ### Check status across all providers [Section titled “Check status across all providers”](#check-status-across-all-providers) Monitor authentication status for all connected providers: * Python ```python 1 def get_user_connection_status(user_id: str, providers: list) -> dict: 2 """Get authentication status for all providers""" 3 status_map = {} 4 5 for provider in providers: 6 try: 7 account = actions.get_connected_account( 8 identifier=user_id, 9 connection_name=provider 10 ) 11 status_map[provider] = { 12 "status": account.status, 13 "last_updated": account.updated_at, 14 "scopes": account.scopes 15 } 16 except Exception as e: 17 status_map[provider] = { 18 "status": "NOT_CONNECTED", 19 "error": str(e) 20 } 21 22 return status_map 23 24 # Usage 25 providers = ["gmail", "slack", "jira", "github"] 26 status = get_user_connection_status("user_123", providers) 27 28 for provider, info in status.items(): 29 print(f"{provider}: {info['status']}") ``` * Node.js ```javascript 1 async function getUserConnectionStatus(userId, providers) { 2 /** 3 * Get authentication status for all providers 4 */ 5 const statusMap = {}; 6 7 for (const provider of providers) { 8 try { 9 const account = await scalekit.actions.getConnectedAccount({ 10 identifier: userId, 11 connectionName: provider 12 }); 13 14 statusMap[provider] = { 15 status: account.status, 16 lastUpdated: account.updatedAt, 17 scopes: account.scopes 18 }; 19 } catch (error) { 20 statusMap[provider] = { 21 status: 'NOT_CONNECTED', 22 error: error.message 23 }; 24 } 25 } 26 27 return statusMap; 28 } 29 30 // Usage 31 const providers = ['gmail', 'slack', 'jira', 'github']; 32 const status = await getUserConnectionStatus('user_123', providers); 33 34 Object.entries(status).forEach(([provider, info]) => { 35 console.log(`${provider}: ${info.status}`); 36 }); ``` * Go ```go 1 func GetUserConnectionStatus(userID string, providers []string) map[string]interface{} { 2 statusMap := make(map[string]interface{}) 3 4 for _, provider := range providers { 5 account, err := scalekitClient.Actions.GetConnectedAccount( 6 context.Background(), 7 userID, 8 provider, 9 ) 10 11 if err != nil { 12 statusMap[provider] = map[string]interface{}{ 13 "status": "NOT_CONNECTED", 14 "error": err.Error(), 15 } 16 } else { 17 statusMap[provider] = map[string]interface{}{ 18 "status": account.Status, 19 "lastUpdated": account.UpdatedAt, 20 "scopes": account.Scopes, 21 } 22 } 23 } 24 25 return statusMap 26 } ``` * Java ```java 1 public Map> getUserConnectionStatus( 2 String userId, List providers 3 ) { 4 Map> statusMap = new HashMap<>(); 5 6 for (String provider : providers) { 7 try { 8 ConnectedAccount account = scalekitClient.actions() 9 .getConnectedAccount(userId, provider); 10 11 Map info = new HashMap<>(); 12 info.put("status", account.getStatus()); 13 info.put("lastUpdated", account.getUpdatedAt()); 14 info.put("scopes", account.getScopes()); 15 statusMap.put(provider, info); 16 } catch (Exception e) { 17 Map info = new HashMap<>(); 18 info.put("status", "NOT_CONNECTED"); 19 info.put("error", e.getMessage()); 20 statusMap.put(provider, info); 21 } 22 } 23 24 return statusMap; 25 } ``` ## Handling different authentication states [Section titled “Handling different authentication states”](#handling-different-authentication-states) Different providers may have different states simultaneously: ```python 1 # Example: User's connection status 2 { 3 "gmail": "ACTIVE", # Working normally 4 "slack": "EXPIRED", # Needs token refresh 5 "jira": "PENDING", # User hasn't authorized yet 6 "github": "REVOKED" # User revoked access 7 } ``` ### Implement state-aware logic [Section titled “Implement state-aware logic”](#implement-state-aware-logic) ```python 1 def execute_multi_provider_workflow(user_id: str): 2 """ 3 Execute workflow requiring multiple providers. 4 Handle different authentication states gracefully. 5 """ 6 providers_status = { 7 "gmail": None, 8 "slack": None, 9 "jira": None 10 } 11 12 # Check status of all required providers 13 for provider in providers_status.keys(): 14 try: 15 account = actions.get_connected_account( 16 identifier=user_id, 17 connection_name=provider 18 ) 19 providers_status[provider] = account.status 20 except Exception: 21 providers_status[provider] = "NOT_CONNECTED" 22 23 # Determine what actions are possible 24 can_send_email = providers_status["gmail"] == "ACTIVE" 25 can_notify_slack = providers_status["slack"] == "ACTIVE" 26 can_create_ticket = providers_status["jira"] == "ACTIVE" 27 28 # Execute workflow with graceful degradation 29 results = {} 30 31 if can_send_email: 32 results["email"] = actions.execute_tool( 33 identifier=user_id, 34 tool_name="gmail_send_email", 35 tool_input={"to": "team@example.com", "subject": "Update"} 36 ) 37 else: 38 results["email"] = {"error": "Gmail not connected"} 39 40 if can_notify_slack: 41 results["slack"] = actions.execute_tool( 42 identifier=user_id, 43 tool_name="slack_send_message", 44 tool_input={"channel": "#general", "text": "Update posted"} 45 ) 46 else: 47 results["slack"] = {"error": "Slack not connected"} 48 49 if can_create_ticket: 50 results["jira"] = actions.execute_tool( 51 identifier=user_id, 52 tool_name="jira_create_issue", 53 tool_input={"project": "SUPPORT", "summary": "Customer inquiry"} 54 ) 55 else: 56 results["jira"] = {"error": "Jira not connected"} 57 58 # Report results to user 59 return { 60 "completed": [k for k, v in results.items() if "error" not in v], 61 "failed": [k for k, v in results.items() if "error" in v], 62 "details": results 63 } 64 65 # Usage 66 result = execute_multi_provider_workflow("user_123") 67 print(f"Completed: {result['completed']}") 68 print(f"Failed: {result['failed']}") ``` ## User experience patterns [Section titled “User experience patterns”](#user-experience-patterns) ### Connection management dashboard [Section titled “Connection management dashboard”](#connection-management-dashboard) Display all provider connections in user settings: ```python 1 def get_connection_dashboard_data(user_id: str) -> dict: 2 """Get data for user's connection management dashboard""" 3 supported_providers = ["gmail", "slack", "jira", "github", "calendar"] 4 5 dashboard_data = [] 6 7 for provider in supported_providers: 8 try: 9 account = actions.get_connected_account( 10 identifier=user_id, 11 connection_name=provider 12 ) 13 14 dashboard_data.append({ 15 "provider": provider, 16 "connected": True, 17 "status": account.status, 18 "last_updated": account.updated_at, 19 "can_reconnect": account.status in ["EXPIRED", "REVOKED"], 20 "reconnect_link": None if account.status == "ACTIVE" else 21 actions.get_authorization_link( 22 connection_name=provider, 23 identifier=user_id 24 ).link 25 }) 26 except Exception: 27 dashboard_data.append({ 28 "provider": provider, 29 "connected": False, 30 "status": "NOT_CONNECTED", 31 "connect_link": actions.get_authorization_link( 32 connection_name=provider, 33 identifier=user_id 34 ).link 35 }) 36 37 return { 38 "user_id": user_id, 39 "connections": dashboard_data, 40 "total_connected": sum(1 for c in dashboard_data if c["connected"]), 41 "needs_attention": sum( 42 1 for c in dashboard_data 43 if c.get("can_reconnect", False) 44 ) 45 } 46 47 # Usage - send this data to your frontend 48 dashboard = get_connection_dashboard_data("user_123") ``` ### Progressive connection onboarding [Section titled “Progressive connection onboarding”](#progressive-connection-onboarding) Guide users to connect providers as needed: ```python 1 def get_required_connections_for_feature(feature: str) -> list: 2 """Map features to required provider connections""" 3 feature_requirements = { 4 "email_automation": ["gmail"], 5 "team_notifications": ["slack"], 6 "project_sync": ["jira", "github"], 7 "calendar_scheduling": ["calendar"], 8 "full_productivity": ["gmail", "slack", "jira", "calendar", "github"] 9 } 10 11 return feature_requirements.get(feature, []) 12 13 def check_user_ready_for_feature(user_id: str, feature: str) -> dict: 14 """Check if user has connected all providers needed for feature""" 15 required_providers = get_required_connections_for_feature(feature) 16 17 connection_status = {} 18 missing_connections = [] 19 20 for provider in required_providers: 21 try: 22 account = actions.get_connected_account( 23 identifier=user_id, 24 connection_name=provider 25 ) 26 is_active = account.status == "ACTIVE" 27 connection_status[provider] = is_active 28 29 if not is_active: 30 missing_connections.append({ 31 "provider": provider, 32 "status": account.status, 33 "link": actions.get_authorization_link( 34 connection_name=provider, 35 identifier=user_id 36 ).link 37 }) 38 except Exception: 39 connection_status[provider] = False 40 missing_connections.append({ 41 "provider": provider, 42 "status": "NOT_CONNECTED", 43 "link": actions.get_authorization_link( 44 connection_name=provider, 45 identifier=user_id 46 ).link 47 }) 48 49 return { 50 "feature": feature, 51 "ready": len(missing_connections) == 0, 52 "connection_status": connection_status, 53 "missing_connections": missing_connections 54 } 55 56 # Usage 57 readiness = check_user_ready_for_feature("user_123", "email_automation") 58 if not readiness["ready"]: 59 print("Please connect the following providers:") 60 for conn in readiness["missing_connections"]: 61 print(f" - {conn['provider']}: {conn['link']}") ``` ## Bulk operations [Section titled “Bulk operations”](#bulk-operations) Execute operations across multiple providers efficiently: * Python ```python 1 def send_notification_to_all_channels(user_id: str, message: str): 2 """Send notification via all connected messaging platforms""" 3 messaging_providers = { 4 "slack": "slack_send_message", 5 "teams": "teams_send_message", 6 "discord": "discord_send_message" 7 } 8 9 results = {} 10 11 for provider, tool_name in messaging_providers.items(): 12 try: 13 # Check if provider is connected 14 account = actions.get_connected_account( 15 identifier=user_id, 16 connection_name=provider 17 ) 18 19 if account.status == "ACTIVE": 20 # Execute tool 21 result = actions.execute_tool( 22 identifier=user_id, 23 tool_name=tool_name, 24 tool_input={"text": message, "channel": "#notifications"} 25 ) 26 results[provider] = {"success": True, "result": result} 27 else: 28 results[provider] = { 29 "success": False, 30 "error": f"Not connected (status: {account.status})" 31 } 32 except Exception as e: 33 results[provider] = {"success": False, "error": str(e)} 34 35 return results 36 37 # Usage 38 notification_results = send_notification_to_all_channels( 39 "user_123", 40 "Deployment completed successfully!" 41 ) ``` * Node.js ```javascript 1 async function sendNotificationToAllChannels(userId, message) { 2 /** 3 * Send notification via all connected messaging platforms 4 */ 5 const messagingProviders = { 6 slack: 'slack_send_message', 7 teams: 'teams_send_message', 8 discord: 'discord_send_message' 9 }; 10 11 const results = {}; 12 13 for (const [provider, toolName] of Object.entries(messagingProviders)) { 14 try { 15 // Check if provider is connected 16 const account = await scalekit.actions.getConnectedAccount({ 17 identifier: userId, 18 connectionName: provider 19 }); 20 21 if (account.status === 'ACTIVE') { 22 // Execute tool 23 const result = await scalekit.actions.executeTool({ 24 identifier: userId, 25 toolName: toolName, 26 toolInput: { text: message, channel: '#notifications' } 27 }); 28 results[provider] = { success: true, result }; 29 } else { 30 results[provider] = { 31 success: false, 32 error: `Not connected (status: ${account.status})` 33 }; 34 } 35 } catch (error) { 36 results[provider] = { success: false, error: error.message }; 37 } 38 } 39 40 return results; 41 } ``` * Go ```go 1 func SendNotificationToAllChannels(userID, message string) map[string]interface{} { 2 messagingProviders := map[string]string{ 3 "slack": "slack_send_message", 4 "teams": "teams_send_message", 5 "discord": "discord_send_message", 6 } 7 8 results := make(map[string]interface{}) 9 10 for provider, toolName := range messagingProviders { 11 account, err := scalekitClient.Actions.GetConnectedAccount( 12 context.Background(), 13 userID, 14 provider, 15 ) 16 17 if err != nil { 18 results[provider] = map[string]interface{}{ 19 "success": false, 20 "error": err.Error(), 21 } 22 continue 23 } 24 25 if account.Status == "ACTIVE" { 26 result, err := scalekitClient.Actions.ExecuteTool( 27 context.Background(), 28 userID, 29 toolName, 30 map[string]interface{}{ 31 "text": message, 32 "channel": "#notifications", 33 }, 34 ) 35 36 if err != nil { 37 results[provider] = map[string]interface{}{ 38 "success": false, 39 "error": err.Error(), 40 } 41 } else { 42 results[provider] = map[string]interface{}{ 43 "success": true, 44 "result": result, 45 } 46 } 47 } 48 } 49 50 return results 51 } ``` * Java ```java 1 public Map> sendNotificationToAllChannels( 2 String userId, String message 3 ) { 4 Map messagingProviders = Map.of( 5 "slack", "slack_send_message", 6 "teams", "teams_send_message", 7 "discord", "discord_send_message" 8 ); 9 10 Map> results = new HashMap<>(); 11 12 for (Map.Entry entry : messagingProviders.entrySet()) { 13 String provider = entry.getKey(); 14 String toolName = entry.getValue(); 15 16 try { 17 ConnectedAccount account = scalekitClient.actions() 18 .getConnectedAccount(userId, provider); 19 20 if ("ACTIVE".equals(account.getStatus())) { 21 Map toolInput = Map.of( 22 "text", message, 23 "channel", "#notifications" 24 ); 25 26 ToolResult result = scalekitClient.actions() 27 .executeTool(userId, toolName, toolInput); 28 29 results.put(provider, Map.of("success", true, "result", result)); 30 } else { 31 results.put(provider, Map.of( 32 "success", false, 33 "error", "Not connected (status: " + account.getStatus() + ")" 34 )); 35 } 36 } catch (Exception e) { 37 results.put(provider, Map.of("success", false, "error", e.getMessage())); 38 } 39 } 40 41 return results; 42 } ``` ## Best practices [Section titled “Best practices”](#best-practices) ### Graceful degradation [Section titled “Graceful degradation”](#graceful-degradation) Design workflows that degrade gracefully when providers aren’t connected: ```python 1 # Good: Workflow continues with available providers 2 if gmail_connected: 3 send_email() 4 if slack_connected: 5 notify_slack() 6 # User gets partial functionality 7 8 # Bad: Workflow fails completely 9 if not (gmail_connected and slack_connected): 10 raise Error("Connect all providers first") ``` ### Clear status communication [Section titled “Clear status communication”](#clear-status-communication) Show users which providers are connected and which need attention: ```python 1 dashboard_message = f""" 2 Your Connections: 3 ✓ Gmail: Connected and working 4 ⚠ Slack: Token expired - reconnect now 5 ✗ Jira: Not connected - connect to enable tickets 6 ✓ Calendar: Connected and working 7 """ ``` ### Proactive reconnection prompts [Section titled “Proactive reconnection prompts”](#proactive-reconnection-prompts) Notify users before connections become critical: ```python 1 def check_and_notify_expiring_connections(user_id: str): 2 """Check for connections that need attention""" 3 providers = ["gmail", "slack", "jira", "calendar"] 4 5 needs_attention = [] 6 7 for provider in providers: 8 try: 9 account = actions.get_connected_account( 10 identifier=user_id, 11 connection_name=provider 12 ) 13 14 if account.status in ["EXPIRED", "REVOKED"]: 15 needs_attention.append({ 16 "provider": provider, 17 "status": account.status, 18 "reconnect_link": actions.get_authorization_link( 19 connection_name=provider, 20 identifier=user_id 21 ).link 22 }) 23 except Exception: 24 continue 25 26 if needs_attention: 27 # Send notification to user 28 print(f"⚠ {len(needs_attention)} connection(s) need your attention") 29 for conn in needs_attention: 30 print(f" - {conn['provider']}: {conn['status']}") 31 32 return needs_attention ``` ## Next steps [Section titled “Next steps”](#next-steps) * [Testing Authentication](/agentkit/authentication/testing-auth-flows) - Testing multi-provider scenarios * [Troubleshooting](/agentkit/authentication/troubleshooting) - Debugging multi-provider issues

---
# DOCUMENT BOUNDARY
---

# Scopes and Permissions

> Learn how to manage OAuth scopes and permissions for AgentKit connections to control what your application can access.

OAuth scopes and permissions determine what data and actions your application can access on behalf of users. Understanding how to properly configure and manage scopes is essential for building secure and functional AgentKit integrations. ## Understanding OAuth scopes [Section titled “Understanding OAuth scopes”](#understanding-oauth-scopes) OAuth scopes are permission grants that define the level of access your application has to a user’s data with third-party providers. ### What are scopes? [Section titled “What are scopes?”](#what-are-scopes) Scopes are strings that represent specific permissions: ```plaintext 1 # Example OAuth scopes 2 https://www.googleapis.com/auth/gmail.readonly # Read Gmail messages 3 https://www.googleapis.com/auth/gmail.send # Send Gmail messages 4 https://www.googleapis.com/auth/calendar.events # Manage calendar events 5 channels:read # Read Slack channels 6 chat:write # Send Slack messages ``` \###How scopes work 1. **Application requests scopes** - Your connection specifies required scopes 2. **User sees consent screen** - Provider shows what permissions are requested 3. **User grants access** - User approves or denies the requested permissions 4. **Tokens include scopes** - Access tokens are limited to granted scopes 5. **API enforces scopes** - Provider APIs check tokens have required scopes ### Scope granularity [Section titled “Scope granularity”](#scope-granularity) Scopes typically follow a hierarchy from broad to specific: **Gmail example:** * `https://mail.google.com/` - Full Gmail access (read, send, delete) * `https://www.googleapis.com/auth/gmail.modify` - Read and modify (but not delete) * `https://www.googleapis.com/auth/gmail.readonly` - Read-only access * `https://www.googleapis.com/auth/gmail.send` - Send emails only ## Provider-specific scopes [Section titled “Provider-specific scopes”](#provider-specific-scopes) Different providers use different scope formats and naming conventions: ### Google Workspace scopes [Section titled “Google Workspace scopes”](#google-workspace-scopes) Google uses URL-based scopes with hierarchical permissions: Gmail Scopes **Read-only access:** ```plaintext 1 https://www.googleapis.com/auth/gmail.readonly ``` **Send emails:** ```plaintext 1 https://www.googleapis.com/auth/gmail.send ``` **Full access:** ```plaintext 1 https://mail.google.com/ ``` **Modify (read/write, no delete):** ```plaintext 1 https://www.googleapis.com/auth/gmail.modify ``` Google Calendar Scopes **Read-only calendar access:** ```plaintext 1 https://www.googleapis.com/auth/calendar.readonly ``` **Manage calendar events:** ```plaintext 1 https://www.googleapis.com/auth/calendar.events ``` **Full calendar access:** ```plaintext 1 https://www.googleapis.com/auth/calendar ``` Google Drive Scopes **Read-only access:** ```plaintext 1 https://www.googleapis.com/auth/drive.readonly ``` **Per-file access:** ```plaintext 1 https://www.googleapis.com/auth/drive.file ``` **Full drive access:** ```plaintext 1 https://www.googleapis.com/auth/drive ``` Google Sheets Scopes **Read-only sheets:** ```plaintext 1 https://www.googleapis.com/auth/spreadsheets.readonly ``` **Edit sheets:** ```plaintext 1 https://www.googleapis.com/auth/spreadsheets ``` ### Microsoft 365 scopes [Section titled “Microsoft 365 scopes”](#microsoft-365-scopes) Microsoft uses dotted notation with resource.permission format: Outlook/Mail Scopes **Read mail:** ```plaintext 1 Mail.Read ``` **Send mail:** ```plaintext 1 Mail.Send ``` **Read/write mail:** ```plaintext 1 Mail.ReadWrite ``` Calendar Scopes **Read calendar:** ```plaintext 1 Calendars.Read ``` **Manage calendar:** ```plaintext 1 Calendars.ReadWrite ``` OneDrive Scopes **Read files:** ```plaintext 1 Files.Read.All ``` **Read/write files:** ```plaintext 1 Files.ReadWrite.All ``` Teams Scopes **Read teams:** ```plaintext 1 Team.ReadBasic.All ``` **Send messages:** ```plaintext 1 ChannelMessage.Send ``` ### Slack scopes [Section titled “Slack scopes”](#slack-scopes) Slack uses simple string-based scopes: Channel Scopes **Read channels:** ```plaintext 1 channels:read ``` **Manage channels:** ```plaintext 1 channels:manage ``` **Join channels:** ```plaintext 1 channels:join ``` Chat Scopes **Send messages:** ```plaintext 1 chat:write ``` **Send as user:** ```plaintext 1 chat:write.customize ``` User Scopes **Read user info:** ```plaintext 1 users:read ``` **Read user email:** ```plaintext 1 users:read.email ``` File Scopes **Read files:** ```plaintext 1 files:read ``` **Write files:** ```plaintext 1 files:write ``` ### Jira/Atlassian scopes [Section titled “Jira/Atlassian scopes”](#jiraatlassian-scopes) Atlassian uses colon-separated scopes: ```plaintext 1 read:jira-work # Read issues and projects 2 write:jira-work # Create and update issues 3 read:jira-user # Read user information 4 manage:jira-project # Manage projects ``` ## Configuring scopes in connections [Section titled “Configuring scopes in connections”](#configuring-scopes-in-connections) Scopes are configured at the connection level in Scalekit: ### Using Scalekit dashboard [Section titled “Using Scalekit dashboard”](#using-scalekit-dashboard) 1. Navigate to **AgentKit** > **Connections** 2. Select your connection or create a new one 3. In the **Scopes** section, enter required scopes 4. Scopes vary by provider - refer to provider’s documentation 5. Save the connection configuration 6. Existing users must re-authenticate to get new scopes ### Scope configuration examples [Section titled “Scope configuration examples”](#scope-configuration-examples) **Gmail connection with multiple scopes:** ```javascript 1 // Dashboard configuration (for reference) 2 { 3 "connection_name": "gmail", 4 "provider": "GMAIL", 5 "scopes": [ 6 "https://www.googleapis.com/auth/gmail.readonly", 7 "https://www.googleapis.com/auth/gmail.send", 8 "https://www.googleapis.com/auth/gmail.modify" 9 ] 10 } ``` **Slack connection with workspace scopes:** ```javascript 1 // Dashboard configuration (for reference) 2 { 3 "connection_name": "slack", 4 "provider": "SLACK", 5 "scopes": [ 6 "channels:read", 7 "chat:write", 8 "users:read", 9 "files:read" 10 ] 11 } ``` ## Checking granted scopes [Section titled “Checking granted scopes”](#checking-granted-scopes) Verify which scopes a user has granted: * Python ```python 1 # Get connected account and check granted scopes 2 account = actions.get_connected_account( 3 identifier="user_123", 4 connection_name="gmail" 5 ) 6 7 print(f"Granted scopes: {account.scopes}") 8 9 # Check if specific scope is granted 10 required_scope = "https://www.googleapis.com/auth/gmail.send" 11 if required_scope in account.scopes: 12 print("✓ User granted email sending permission") 13 else: 14 print("✗ Email sending permission not granted") 15 # Request re-authentication with required scope ``` * Node.js ```javascript 1 // Get connected account and check granted scopes 2 const account = await scalekit.actions.getConnectedAccount({ 3 identifier: 'user_123', 4 connectionName: 'gmail' 5 }); 6 7 console.log(`Granted scopes: ${account.scopes}`); 8 9 // Check if specific scope is granted 10 const requiredScope = 'https://www.googleapis.com/auth/gmail.send'; 11 if (account.scopes.includes(requiredScope)) { 12 console.log('✓ User granted email sending permission'); 13 } else { 14 console.log('✗ Email sending permission not granted'); 15 // Request re-authentication with required scope 16 } ``` * Go ```go 1 // Get connected account and check granted scopes 2 account, err := scalekitClient.Actions.GetConnectedAccount( 3 context.Background(), 4 "user_123", 5 "gmail", 6 ) 7 if err != nil { 8 log.Fatal(err) 9 } 10 11 fmt.Printf("Granted scopes: %v\n", account.Scopes) 12 13 // Check if specific scope is granted 14 requiredScope := "https://www.googleapis.com/auth/gmail.send" 15 hasScope := false 16 for _, scope := range account.Scopes { 17 if scope == requiredScope { 18 hasScope = true 19 break 20 } 21 } 22 23 if hasScope { 24 fmt.Println("✓ User granted email sending permission") 25 } else { 26 fmt.Println("✗ Email sending permission not granted") 27 } ``` * Java ```java 1 // Get connected account and check granted scopes 2 ConnectedAccount account = scalekitClient.actions().getConnectedAccount( 3 "user_123", 4 "gmail" 5 ); 6 7 System.out.println("Granted scopes: " + account.getScopes()); 8 9 // Check if specific scope is granted 10 String requiredScope = "https://www.googleapis.com/auth/gmail.send"; 11 if (account.getScopes().contains(requiredScope)) { 12 System.out.println("✓ User granted email sending permission"); 13 } else { 14 System.out.println("✗ Email sending permission not granted"); 15 // Request re-authentication with required scope 16 } ``` ## Requesting additional scopes [Section titled “Requesting additional scopes”](#requesting-additional-scopes) When you need additional permissions, users must re-authenticate: ### Scope upgrade flow [Section titled “Scope upgrade flow”](#scope-upgrade-flow) 1. **Update connection** - Add new scopes to connection configuration 2. **Detect missing scopes** - Check connected account for required scopes 3. **Generate auth link** - Create new authorization link for user 4. **User re-authenticates** - User approves additional permissions 5. **Verify new scopes** - Confirm scopes were granted ### Implementation example [Section titled “Implementation example”](#implementation-example) * Python ```python 1 def ensure_required_scopes(identifier: str, connection_name: str, required_scopes: list): 2 """ 3 Ensure user has granted all required scopes. 4 Returns True if all scopes granted, False if re-authentication needed. 5 """ 6 # Get current account and scopes 7 account = actions.get_connected_account( 8 identifier=identifier, 9 connection_name=connection_name 10 ) 11 12 # Check if all required scopes are granted 13 granted_scopes = set(account.scopes) 14 missing_scopes = [s for s in required_scopes if s not in granted_scopes] 15 16 if not missing_scopes: 17 print("✓ All required scopes granted") 18 return True 19 20 print(f"⚠ Missing scopes: {missing_scopes}") 21 22 # Generate authorization link for re-authentication 23 link_response = actions.get_authorization_link( 24 connection_name=connection_name, 25 identifier=identifier 26 ) 27 28 print(f"🔗 User must re-authorize with additional permissions:") 29 print(f" {link_response.link}") 30 print(f"\nMissing permissions:") 31 for scope in missing_scopes: 32 print(f" - {scope}") 33 34 return False 35 36 # Usage 37 required_scopes = [ 38 "https://www.googleapis.com/auth/gmail.readonly", 39 "https://www.googleapis.com/auth/gmail.send", 40 "https://www.googleapis.com/auth/gmail.modify" 41 ] 42 43 if ensure_required_scopes("user_123", "gmail", required_scopes): 44 # All scopes granted, proceed with operation 45 result = actions.execute_tool(...) 46 else: 47 # Waiting for user to re-authenticate 48 print("Please authorize additional permissions") ``` * Node.js ```javascript 1 async function ensureRequiredScopes(identifier, connectionName, requiredScopes) { 2 /** 3 * Ensure user has granted all required scopes. 4 * Returns true if all scopes granted, false if re-authentication needed. 5 */ 6 // Get current account and scopes 7 const account = await scalekit.actions.getConnectedAccount({ 8 identifier, 9 connectionName 10 }); 11 12 // Check if all required scopes are granted 13 const grantedScopes = new Set(account.scopes); 14 const missingScopes = requiredScopes.filter(s => !grantedScopes.has(s)); 15 16 if (missingScopes.length === 0) { 17 console.log('✓ All required scopes granted'); 18 return true; 19 } 20 21 console.log(`⚠ Missing scopes: ${missingScopes.join(', ')}`); 22 23 // Generate authorization link for re-authentication 24 const linkResponse = await scalekit.actions.getAuthorizationLink({ 25 connectionName, 26 identifier 27 }); 28 29 console.log('🔗 User must re-authorize with additional permissions:'); 30 console.log(` ${linkResponse.link}`); 31 console.log('\nMissing permissions:'); 32 missingScopes.forEach(scope => console.log(` - ${scope}`)); 33 34 return false; 35 } 36 37 // Usage 38 const requiredScopes = [ 39 'https://www.googleapis.com/auth/gmail.readonly', 40 'https://www.googleapis.com/auth/gmail.send', 41 'https://www.googleapis.com/auth/gmail.modify' 42 ]; 43 44 if (await ensureRequiredScopes('user_123', 'gmail', requiredScopes)) { 45 // All scopes granted, proceed with operation 46 const result = await scalekit.actions.executeTool(...); 47 } else { 48 // Waiting for user to re-authenticate 49 console.log('Please authorize additional permissions'); 50 } ``` * Go ```go 1 func ensureRequiredScopes(identifier, connectionName string, requiredScopes []string) (bool, error) { 2 // Get current account and scopes 3 account, err := scalekitClient.Actions.GetConnectedAccount( 4 context.Background(), 5 identifier, 6 connectionName, 7 ) 8 if err != nil { 9 return false, err 10 } 11 12 // Check if all required scopes are granted 13 grantedScopes := make(map[string]bool) 14 for _, scope := range account.Scopes { 15 grantedScopes[scope] = true 16 } 17 18 var missingScopes []string 19 for _, scope := range requiredScopes { 20 if !grantedScopes[scope] { 21 missingScopes = append(missingScopes, scope) 22 } 23 } 24 25 if len(missingScopes) == 0 { 26 fmt.Println("✓ All required scopes granted") 27 return true, nil 28 } 29 30 fmt.Printf("⚠ Missing scopes: %v\n", missingScopes) 31 32 // Generate authorization link 33 linkResponse, err := scalekitClient.Actions.GetAuthorizationLink( 34 context.Background(), 35 connectionName, 36 identifier, 37 ) 38 if err != nil { 39 return false, err 40 } 41 42 fmt.Printf("🔗 User must re-authorize: %s\n", linkResponse.Link) 43 44 return false, nil 45 } ``` * Java ```java 1 public boolean ensureRequiredScopes(String identifier, String connectionName, List requiredScopes) { 2 try { 3 // Get current account and scopes 4 ConnectedAccount account = scalekitClient.actions().getConnectedAccount( 5 identifier, 6 connectionName 7 ); 8 9 // Check if all required scopes are granted 10 Set grantedScopes = new HashSet<>(account.getScopes()); 11 List missingScopes = requiredScopes.stream() 12 .filter(s -> !grantedScopes.contains(s)) 13 .collect(Collectors.toList()); 14 15 if (missingScopes.isEmpty()) { 16 System.out.println("✓ All required scopes granted"); 17 return true; 18 } 19 20 System.out.println("⚠ Missing scopes: " + String.join(", ", missingScopes)); 21 22 // Generate authorization link 23 AuthorizationLink linkResponse = scalekitClient.actions().getAuthorizationLink( 24 connectionName, 25 identifier 26 ); 27 28 System.out.println("🔗 User must re-authorize: " + linkResponse.getLink()); 29 System.out.println("\nMissing permissions:"); 30 missingScopes.forEach(scope -> System.out.println(" - " + scope)); 31 32 return false; 33 } catch (Exception e) { 34 System.err.println("Error checking scopes: " + e.getMessage()); 35 return false; 36 } 37 } ``` ## Scope validation before tool execution [Section titled “Scope validation before tool execution”](#scope-validation-before-tool-execution) Always validate scopes before executing tools to provide better error messages: ```python 1 # Map tools to required scopes 2 TOOL_SCOPE_REQUIREMENTS = { 3 'gmail_send_email': ['https://www.googleapis.com/auth/gmail.send'], 4 'gmail_fetch_mails': ['https://www.googleapis.com/auth/gmail.readonly'], 5 'gmail_delete_email': ['https://mail.google.com/'], 6 'calendar_create_event': ['https://www.googleapis.com/auth/calendar.events'], 7 'slack_send_message': ['chat:write'], 8 } 9 10 def execute_tool_with_scope_check(identifier, connection_name, tool_name, tool_input): 11 """Execute tool after validating required scopes""" 12 # Get required scopes for this tool 13 required_scopes = TOOL_SCOPE_REQUIREMENTS.get(tool_name, []) 14 15 if required_scopes: 16 # Verify user has granted required scopes 17 account = actions.get_connected_account( 18 identifier=identifier, 19 connection_name=connection_name 20 ) 21 22 granted_scopes = set(account.scopes) 23 missing_scopes = [s for s in required_scopes if s not in granted_scopes] 24 25 if missing_scopes: 26 raise PermissionError( 27 f"Missing required permissions for {tool_name}: {missing_scopes}. " 28 f"Please re-authorize to grant these permissions." 29 ) 30 31 # Scopes verified, execute tool 32 return actions.execute_tool( 33 identifier=identifier, 34 tool_name=tool_name, 35 tool_input=tool_input 36 ) 37 38 # Usage 39 try: 40 result = execute_tool_with_scope_check( 41 identifier="user_123", 42 connection_name="gmail", 43 tool_name="gmail_send_email", 44 tool_input={"to": "user@example.com", "subject": "Test", "body": "Hello"} 45 ) 46 print("✓ Email sent successfully") 47 except PermissionError as e: 48 print(f"✗ Permission error: {e}") 49 # Prompt user to re-authorize ``` ## Best practices [Section titled “Best practices”](#best-practices) ### Request minimum necessary scopes [Section titled “Request minimum necessary scopes”](#request-minimum-necessary-scopes) **Good:** ```python 1 # Only request scopes you need 2 scopes = [ 3 "https://www.googleapis.com/auth/gmail.readonly", # For reading emails 4 "https://www.googleapis.com/auth/gmail.send" # For sending emails 5 ] ``` **Avoid:** ```python 1 # Don't request overly broad access 2 scopes = [ 3 "https://mail.google.com/" # Full Gmail access including delete 4 ] ``` ### Explain permissions to users [Section titled “Explain permissions to users”](#explain-permissions-to-users) Provide clear explanations of why you need specific permissions: ```python 1 SCOPE_EXPLANATIONS = { 2 "https://www.googleapis.com/auth/gmail.readonly": 3 "Read your emails to analyze and summarize them", 4 "https://www.googleapis.com/auth/gmail.send": 5 "Send emails on your behalf", 6 "https://www.googleapis.com/auth/calendar.events": 7 "Create and manage calendar events for you", 8 "chat:write": 9 "Send messages in Slack channels", 10 } 11 12 # Show explanations in your UI before redirecting to OAuth 13 def get_scope_explanation(scope): 14 return SCOPE_EXPLANATIONS.get(scope, "Access your account data") ``` ### Handle scope denials gracefully [Section titled “Handle scope denials gracefully”](#handle-scope-denials-gracefully) ```python 1 # After OAuth callback 2 if user_denied_scopes: 3 # Don't show error - explain what features won't work 4 message = """ 5 Some features will be limited because certain permissions weren't granted: 6 - Email sending: Requires 'Send email' permission 7 - Email reading: Requires 'Read email' permission 8 9 You can grant these permissions later in Settings. 10 """ 11 # Provide link to re-authorize in settings ``` ### Incremental authorization [Section titled “Incremental authorization”](#incremental-authorization) Request additional scopes only when needed: ```python 1 # Start with minimal scopes 2 initial_scopes = ["https://www.googleapis.com/auth/gmail.readonly"] 3 4 # Later, when user wants to send email 5 if user_wants_to_send_email: 6 # Request additional scope 7 additional_scopes = ["https://www.googleapis.com/auth/gmail.send"] 8 # Prompt user to grant additional permission ``` ## Troubleshooting scope issues [Section titled “Troubleshooting scope issues”](#troubleshooting-scope-issues) ### Insufficient permissions error [Section titled “Insufficient permissions error”](#insufficient-permissions-error) **Error:** “Insufficient permissions” or 403 Forbidden **Solution:** 1. Check which scopes are currently granted 2. Verify the tool requires those specific scopes 3. Update connection configuration if needed 4. Have user re-authenticate to grant additional scopes ### Scope not available for provider [Section titled “Scope not available for provider”](#scope-not-available-for-provider) **Error:** Invalid scope or scope not recognized **Solution:** 1. Verify scope name matches provider’s documentation exactly 2. Check if scope requires special provider approval 3. Some scopes only available to verified applications 4. Review provider’s scope documentation for correct format ### User sees unexpected consent screen [Section titled “User sees unexpected consent screen”](#user-sees-unexpected-consent-screen) **Issue:** OAuth consent shows different or additional permissions **Causes:** * Scopes configured in connection don’t match expected * Provider groups related scopes together * Sensitive scopes trigger additional consent **Solution:** * Review connection scope configuration * Check provider’s scope grouping behavior * Ensure sensitive scopes are truly necessary ## Next steps [Section titled “Next steps”](#next-steps) * [Authentication Troubleshooting](/agentkit/authentication/troubleshooting) - Debugging auth issues * [Multi-Provider Authentication](/agentkit/authentication/multi-provider) - Managing multiple provider connections

---
# DOCUMENT BOUNDARY
---

# Testing Authentication Flows

> Learn how to test AgentKit authentication flows in development, staging, and production environments with comprehensive testing strategies.

Thorough testing of authentication flows ensures your AgentKit integration works reliably before production deployment. This guide covers testing strategies, tools, and best practices. ## Testing environments [Section titled “Testing environments”](#testing-environments) ### Development environment [Section titled “Development environment”](#development-environment) **Purpose:** Rapid iteration and debugging **Characteristics:** * Local development server * Test accounts and credentials * Verbose logging enabled * Quick feedback loops **Setup:** development.env ```python 1 SCALEKIT_ENV_URL=https://your-env.scalekit.dev 2 SCALEKIT_CLIENT_ID=dev_client_id 3 SCALEKIT_CLIENT_SECRET=dev_client_secret 4 DEBUG=true 5 LOG_LEVEL=debug ``` ### Staging environment [Section titled “Staging environment”](#staging-environment) **Purpose:** Pre-production validation **Characteristics:** * Production-like configuration * Realistic data volumes * Integration with staging third-party accounts * Performance testing **Setup:** staging.env ```python 1 SCALEKIT_ENV_URL=https://your-env.scalekit.cloud 2 SCALEKIT_CLIENT_ID=staging_client_id 3 SCALEKIT_CLIENT_SECRET=staging_client_secret 4 DEBUG=false 5 LOG_LEVEL=info ``` ### Production environment [Section titled “Production environment”](#production-environment) **Purpose:** Live user traffic **Characteristics:** * Real user data * Verified OAuth applications * Monitoring and alerts * Minimal logging **Setup:** production.env ```python 1 SCALEKIT_ENV_URL=https://your-env.scalekit.cloud 2 SCALEKIT_CLIENT_ID=prod_client_id 3 SCALEKIT_CLIENT_SECRET=prod_client_secret 4 DEBUG=false 5 LOG_LEVEL=warn ``` ## Test account setup [Section titled “Test account setup”](#test-account-setup) ### Creating test providers [Section titled “Creating test providers”](#creating-test-providers) Set up test accounts for each provider: **Google Workspace:** 1. Create test Google account 2. Enable 2FA if testing MFA scenarios 3. Use for Gmail, Calendar, Drive testing **Slack:** 1. Create free Slack workspace 2. Install your Slack app 3. Use for messaging and notification testing **Microsoft 365:** 1. Get Microsoft 365 developer account (free) 2. Create test users 3. Use for Outlook, Teams, OneDrive testing **Jira/Atlassian:** 1. Create free Atlassian Cloud account 2. Set up test projects 3. Generate API tokens for testing ### Test user patterns [Section titled “Test user patterns”](#test-user-patterns) Create different test users for scenarios: ```python 1 # Test user configurations 2 TEST_USERS = { 3 "basic_user": { 4 "identifier": "test_user_001", 5 "providers": ["gmail"], 6 "scenario": "Single provider, basic authentication" 7 }, 8 "power_user": { 9 "identifier": "test_user_002", 10 "providers": ["gmail", "slack", "jira", "calendar"], 11 "scenario": "Multiple providers, full feature access" 12 }, 13 "expired_user": { 14 "identifier": "test_user_003", 15 "providers": ["gmail"], 16 "scenario": "Expired tokens, test refresh logic", 17 "setup": "Manually expire tokens" 18 }, 19 "revoked_user": { 20 "identifier": "test_user_004", 21 "providers": ["slack"], 22 "scenario": "User revoked access, test re-auth flow" 23 } 24 } ``` ## Unit testing authentication [Section titled “Unit testing authentication”](#unit-testing-authentication) ### Test connected account creation [Section titled “Test connected account creation”](#test-connected-account-creation) * Python ```python 1 import unittest 2 from unittest.mock import Mock, patch 3 4 class TestConnectedAccountCreation(unittest.TestCase): 5 def setUp(self): 6 self.actions = Mock() 7 self.user_id = "test_user_123" 8 self.provider = "gmail" 9 10 def test_create_connected_account_success(self): 11 """Test successful connected account creation""" 12 # Mock response 13 mock_response = Mock() 14 mock_response.connected_account = Mock( 15 id="account_123", 16 status="PENDING", 17 connection_name="gmail" 18 ) 19 self.actions.get_or_create_connected_account.return_value = mock_response 20 21 # Execute 22 response = self.actions.get_or_create_connected_account( 23 connection_name=self.provider, 24 identifier=self.user_id 25 ) 26 27 # Assert 28 self.assertEqual(response.connected_account.status, "PENDING") 29 self.assertEqual(response.connected_account.connection_name, "gmail") 30 31 def test_generate_authorization_link(self): 32 """Test authorization link generation""" 33 mock_response = Mock() 34 mock_response.link = "https://accounts.google.com/oauth/authorize?..." 35 36 self.actions.get_authorization_link.return_value = mock_response 37 38 response = self.actions.get_authorization_link( 39 connection_name=self.provider, 40 identifier=self.user_id 41 ) 42 43 self.assertIn("https://", response.link) 44 self.actions.get_authorization_link.assert_called_once() 45 46 if __name__ == '__main__': 47 unittest.main() ``` * Node.js ```javascript 1 const { describe, it, expect, jest, beforeEach } = require('@jest/globals'); 2 3 describe('Connected Account Creation', () => { 4 let mockActions; 5 const userId = 'test_user_123'; 6 const provider = 'gmail'; 7 8 beforeEach(() => { 9 mockActions = { 10 getOrCreateConnectedAccount: jest.fn(), 11 getAuthorizationLink: jest.fn() 12 }; 13 }); 14 15 it('should create connected account successfully', async () => { 16 // Mock response 17 const mockResponse = { 18 connectedAccount: { 19 id: 'account_123', 20 status: 'PENDING', 21 connectionName: 'gmail' 22 } 23 }; 24 25 mockActions.getOrCreateConnectedAccount.mockResolvedValue(mockResponse); 26 27 // Execute 28 const response = await mockActions.getOrCreateConnectedAccount({ 29 connectionName: provider, 30 identifier: userId 31 }); 32 33 // Assert 34 expect(response.connectedAccount.status).toBe('PENDING'); 35 expect(response.connectedAccount.connectionName).toBe('gmail'); 36 }); 37 38 it('should generate authorization link', async () => { 39 const mockResponse = { 40 link: 'https://accounts.google.com/oauth/authorize?...' 41 }; 42 43 mockActions.getAuthorizationLink.mockResolvedValue(mockResponse); 44 45 const response = await mockActions.getAuthorizationLink({ 46 connectionName: provider, 47 identifier: userId 48 }); 49 50 expect(response.link).toContain('https://'); 51 expect(mockActions.getAuthorizationLink).toHaveBeenCalledTimes(1); 52 }); 53 }); ``` * Go ```go 1 package auth_test 2 3 import ( 4 "testing" 5 "github.com/stretchr/testify/assert" 6 "github.com/stretchr/testify/mock" 7 ) 8 9 type MockActions struct { 10 mock.Mock 11 } 12 13 func (m *MockActions) GetOrCreateConnectedAccount(connectionName, identifier string) (*ConnectedAccountResponse, error) { 14 args := m.Called(connectionName, identifier) 15 return args.Get(0).(*ConnectedAccountResponse), args.Error(1) 16 } 17 18 func TestCreateConnectedAccount(t *testing.T) { 19 // Arrange 20 mockActions := new(MockActions) 21 userId := "test_user_123" 22 provider := "gmail" 23 24 expectedResponse := &ConnectedAccountResponse{ 25 ConnectedAccount: ConnectedAccount{ 26 ID: "account_123", 27 Status: "PENDING", 28 ConnectionName: "gmail", 29 }, 30 } 31 32 mockActions.On("GetOrCreateConnectedAccount", provider, userId). 33 Return(expectedResponse, nil) 34 35 // Act 36 response, err := mockActions.GetOrCreateConnectedAccount(provider, userId) 37 38 // Assert 39 assert.NoError(t, err) 40 assert.Equal(t, "PENDING", response.ConnectedAccount.Status) 41 assert.Equal(t, "gmail", response.ConnectedAccount.ConnectionName) 42 mockActions.AssertExpectations(t) 43 } ``` * Java ```java 1 import org.junit.jupiter.api.BeforeEach; 2 import org.junit.jupiter.api.Test; 3 import org.mockito.Mock; 4 import org.mockito.MockitoAnnotations; 5 import static org.junit.jupiter.api.Assertions.*; 6 import static org.mockito.Mockito.*; 7 8 class ConnectedAccountCreationTest { 9 @Mock 10 private Actions mockActions; 11 12 private String userId; 13 private String provider; 14 15 @BeforeEach 16 void setUp() { 17 MockitoAnnotations.openMocks(this); 18 userId = "test_user_123"; 19 provider = "gmail"; 20 } 21 22 @Test 23 void testCreateConnectedAccountSuccess() { 24 // Arrange 25 ConnectedAccount account = new ConnectedAccount(); 26 account.setId("account_123"); 27 account.setStatus("PENDING"); 28 account.setConnectionName("gmail"); 29 30 ConnectedAccountResponse mockResponse = new ConnectedAccountResponse(); 31 mockResponse.setConnectedAccount(account); 32 33 when(mockActions.getOrCreateConnectedAccount(provider, userId)) 34 .thenReturn(mockResponse); 35 36 // Act 37 ConnectedAccountResponse response = mockActions 38 .getOrCreateConnectedAccount(provider, userId); 39 40 // Assert 41 assertEquals("PENDING", response.getConnectedAccount().getStatus()); 42 assertEquals("gmail", response.getConnectedAccount().getConnectionName()); 43 verify(mockActions, times(1)).getOrCreateConnectedAccount(provider, userId); 44 } 45 } ``` ### Test token refresh logic [Section titled “Test token refresh logic”](#test-token-refresh-logic) ```python 1 def test_token_refresh_scenarios(self): 2 """Test various token refresh scenarios""" 3 test_cases = [ 4 { 5 "name": "successful_refresh", 6 "initial_status": "EXPIRED", 7 "expected_status": "ACTIVE", 8 "should_succeed": True 9 }, 10 { 11 "name": "refresh_token_invalid", 12 "initial_status": "EXPIRED", 13 "expected_status": "EXPIRED", 14 "should_succeed": False 15 }, 16 { 17 "name": "already_active", 18 "initial_status": "ACTIVE", 19 "expected_status": "ACTIVE", 20 "should_succeed": True 21 } 22 ] 23 24 for case in test_cases: 25 with self.subTest(case=case["name"]): 26 # Setup mock 27 mock_account = Mock() 28 mock_account.status = case["expected_status"] 29 30 if case["should_succeed"]: 31 self.actions.refresh_connected_account.return_value = mock_account 32 else: 33 self.actions.refresh_connected_account.side_effect = Exception("Refresh failed") 34 35 # Execute 36 try: 37 result = self.actions.refresh_connected_account( 38 identifier="test_user", 39 connection_name="gmail" 40 ) 41 success = True 42 except Exception: 43 success = False 44 45 # Assert 46 self.assertEqual(success, case["should_succeed"]) ``` ## Integration testing [Section titled “Integration testing”](#integration-testing) ### Test complete authentication flow [Section titled “Test complete authentication flow”](#test-complete-authentication-flow) ```python 1 import time 2 3 def test_complete_oauth_flow_integration(): 4 """ 5 Integration test for complete OAuth authentication flow. 6 Requires manual intervention for OAuth consent. 7 """ 8 user_id = "integration_test_user" 9 provider = "gmail" 10 11 # Step 1: Create connected account 12 print("Step 1: Creating connected account...") 13 response = actions.get_or_create_connected_account( 14 connection_name=provider, 15 identifier=user_id 16 ) 17 18 account = response.connected_account 19 assert account.status == "PENDING", f"Expected PENDING, got {account.status}" 20 print(f"✓ Connected account created: {account.id}") 21 22 # Step 2: Generate authorization link 23 print("\nStep 2: Generating authorization link...") 24 link_response = actions.get_authorization_link( 25 connection_name=provider, 26 identifier=user_id 27 ) 28 29 print(f"✓ Authorization link: {link_response.link}") 30 print("\n⚠ MANUAL STEP: Open this link in a browser and complete OAuth") 31 print(" Press Enter after completing OAuth flow...") 32 input() 33 34 # Step 3: Verify account is now active 35 print("\nStep 3: Verifying account status...") 36 time.sleep(2) # Brief delay for processing 37 38 account = actions.get_connected_account( 39 identifier=user_id, 40 connection_name=provider 41 ) 42 43 assert account.status == "ACTIVE", f"Expected ACTIVE, got {account.status}" 44 print(f"✓ Account is ACTIVE") 45 print(f" Granted scopes: {account.scopes}") 46 47 # Step 4: Test tool execution 48 print("\nStep 4: Testing tool execution...") 49 result = actions.execute_tool( 50 identifier=user_id, 51 tool_name="gmail_get_profile", 52 tool_input={} 53 ) 54 55 assert result is not None, "Tool execution failed" 56 print(f"✓ Tool executed successfully") 57 58 print("\n✓✓✓ Integration test completed successfully") 59 60 # Run with: pytest test_auth_integration.py -s (to see output) ``` ### Test error scenarios [Section titled “Test error scenarios”](#test-error-scenarios) ```python 1 def test_error_scenarios(): 2 """Test various error scenarios""" 3 user_id = "error_test_user" 4 5 # Test 1: Invalid provider 6 print("Test 1: Invalid provider...") 7 try: 8 actions.get_or_create_connected_account( 9 connection_name="invalid_provider", 10 identifier=user_id 11 ) 12 assert False, "Should have raised error" 13 except Exception as e: 14 print(f"✓ Caught expected error: {type(e).__name__}") 15 16 # Test 2: Execute tool without authentication 17 print("\nTest 2: Tool execution without auth...") 18 try: 19 actions.execute_tool( 20 identifier="nonexistent_user", 21 tool_name="gmail_send_email", 22 tool_input={"to": "test@example.com"} 23 ) 24 assert False, "Should have raised error" 25 except Exception as e: 26 print(f"✓ Caught expected error: {type(e).__name__}") 27 28 # Test 3: Missing required scopes 29 print("\nTest 3: Missing required scopes...") 30 # This test requires setup with insufficient scopes 31 print("⚠ Skipped: Requires special setup") 32 33 print("\n✓✓✓ Error scenario tests completed") ``` ## Automated testing [Section titled “Automated testing”](#automated-testing) ### Test authentication in CI/CD [Section titled “Test authentication in CI/CD”](#test-authentication-in-cicd) .github/workflows/test-auth.yml ```yaml 1 name: Test Authentication Flows 2 3 on: [push, pull_request] 4 5 jobs: 6 test: 7 runs-on: ubuntu-latest 8 9 steps: 10 - uses: actions/checkout@v2 11 12 - name: Set up Python 13 uses: actions/setup-python@v2 14 with: 15 python-version: '3.9' 16 17 - name: Install dependencies 18 run: | 19 pip install -r requirements.txt 20 pip install pytest pytest-cov 21 22 - name: Run unit tests 23 env: 24 SCALEKIT_CLIENT_ID: ${{ secrets.TEST_CLIENT_ID }} 25 SCALEKIT_CLIENT_SECRET: ${{ secrets.TEST_CLIENT_SECRET }} 26 SCALEKIT_ENV_URL: ${{ secrets.TEST_ENV_URL }} 27 run: | 28 pytest tests/test_auth.py -v --cov=src/auth 29 30 - name: Run integration tests (non-OAuth) 31 env: 32 SCALEKIT_CLIENT_ID: ${{ secrets.TEST_CLIENT_ID }} 33 SCALEKIT_CLIENT_SECRET: ${{ secrets.TEST_CLIENT_SECRET }} 34 SCALEKIT_ENV_URL: ${{ secrets.TEST_ENV_URL }} 35 run: | 36 pytest tests/test_auth_integration.py -v -k "not oauth" ``` ### Mock OAuth flows [Section titled “Mock OAuth flows”](#mock-oauth-flows) ```python 1 from unittest.mock import patch, Mock 2 3 def test_oauth_flow_with_mocks(): 4 """Test OAuth flow with mocked responses (no actual OAuth)""" 5 6 with patch('scalekit.actions.get_or_create_connected_account') as mock_create, \ 7 patch('scalekit.actions.get_authorization_link') as mock_link, \ 8 patch('scalekit.actions.get_connected_account') as mock_get: 9 10 # Mock connected account creation 11 mock_account = Mock() 12 mock_account.id = "account_123" 13 mock_account.status = "PENDING" 14 15 mock_response = Mock() 16 mock_response.connected_account = mock_account 17 mock_create.return_value = mock_response 18 19 # Mock authorization link 20 mock_link_response = Mock() 21 mock_link_response.link = "https://mock-oauth-url.com" 22 mock_link.return_value = mock_link_response 23 24 # Mock successful authentication (simulate user completing OAuth) 25 mock_account.status = "ACTIVE" 26 mock_account.scopes = ["gmail.readonly", "gmail.send"] 27 mock_get.return_value = mock_account 28 29 # Test the flow 30 # 1. Create account 31 response = mock_create(connection_name="gmail", identifier="user_123") 32 assert response.connected_account.status == "PENDING" 33 34 # 2. Get auth link 35 link = mock_link(connection_name="gmail", identifier="user_123") 36 assert "https://" in link.link 37 38 # 3. Simulate user completing OAuth (status changes to ACTIVE) 39 account = mock_get(identifier="user_123", connection_name="gmail") 40 assert account.status == "ACTIVE" 41 assert len(account.scopes) > 0 42 43 print("✓ OAuth flow test with mocks completed") ``` ## Performance testing [Section titled “Performance testing”](#performance-testing) ### Test token refresh performance [Section titled “Test token refresh performance”](#test-token-refresh-performance) ```python 1 import time 2 3 def test_token_refresh_performance(): 4 """Measure token refresh latency""" 5 user_id = "perf_test_user" 6 provider = "gmail" 7 8 # Setup: Create account with expired token 9 # (This requires manually setting up an expired account) 10 11 iterations = 10 12 refresh_times = [] 13 14 for i in range(iterations): 15 start_time = time.time() 16 17 try: 18 actions.refresh_connected_account( 19 identifier=user_id, 20 connection_name=provider 21 ) 22 elapsed = time.time() - start_time 23 refresh_times.append(elapsed) 24 print(f"Iteration {i+1}: {elapsed:.3f}s") 25 except Exception as e: 26 print(f"Iteration {i+1} failed: {e}") 27 28 if refresh_times: 29 avg_time = sum(refresh_times) / len(refresh_times) 30 min_time = min(refresh_times) 31 max_time = max(refresh_times) 32 33 print(f"\nToken Refresh Performance:") 34 print(f" Average: {avg_time:.3f}s") 35 print(f" Min: {min_time:.3f}s") 36 print(f" Max: {max_time:.3f}s") 37 38 # Assert reasonable performance (adjust threshold as needed) 39 assert avg_time < 2.0, f"Average refresh time too slow: {avg_time:.3f}s" ``` ## Best practices [Section titled “Best practices”](#best-practices) ### Test checklist [Section titled “Test checklist”](#test-checklist) 1. **Unit tests** - Test individual authentication functions 2. **Integration tests** - Test complete OAuth flows 3. **Error handling** - Test all error scenarios 4. **Token refresh** - Test automatic and manual refresh 5. **Multi-provider** - Test multiple simultaneous connections 6. **Performance** - Measure and optimize latency 7. **Security** - Verify token encryption and secure storage ### Testing dos and don’ts [Section titled “Testing dos and don’ts”](#testing-dos-and-donts) ✅ **Do:** * Use separate test accounts for each provider * Test both success and failure scenarios * Mock external OAuth calls in unit tests * Test token refresh before expiration * Verify error messages are helpful * Test with realistic data volumes ❌ **Don’t:** * Use production accounts for testing * Hardcode test credentials in source code * Skip error scenario testing * Assume OAuth always succeeds * Neglect performance testing * Test only happy path scenarios ### Security testing [Section titled “Security testing”](#security-testing) ```python 1 def test_security_scenarios(): 2 """Test security-related authentication scenarios""" 3 4 # Test 1: Verify tokens are not exposed in logs 5 print("Test 1: Token exposure check...") 6 with patch('logging.Logger.debug') as mock_log: 7 account = actions.get_connected_account( 8 identifier="test_user", 9 connection_name="gmail" 10 ) 11 12 # Verify no access tokens in log calls 13 for call in mock_log.call_args_list: 14 log_message = str(call) 15 assert "access_token" not in log_message.lower() 16 assert "refresh_token" not in log_message.lower() 17 18 print("✓ No tokens in logs") 19 20 # Test 2: Verify HTTPS for OAuth redirects 21 print("\nTest 2: HTTPS verification...") 22 link_response = actions.get_authorization_link( 23 connection_name="gmail", 24 identifier="test_user" 25 ) 26 27 assert link_response.link.startswith("https://") 28 print("✓ OAuth uses HTTPS") 29 30 # Test 3: State parameter validation 31 print("\nTest 3: State parameter present...") 32 assert "state=" in link_response.link 33 print("✓ State parameter included") 34 35 print("\n✓✓✓ Security tests completed") ``` ## Next steps [Section titled “Next steps”](#next-steps) * [Authentication Troubleshooting](/agentkit/authentication/troubleshooting) - Debug authentication issues * [Multi-Provider Authentication](/agentkit/authentication/multi-provider) - Test multiple providers

---
# DOCUMENT BOUNDARY
---

# Authentication Troubleshooting

> Debug and resolve common authentication issues with AgentKit, including OAuth failures, token problems, and provider-specific errors.

This guide helps you diagnose and resolve common authentication issues with AgentKit. Use the troubleshooting steps below to quickly identify and fix problems with connected accounts, OAuth flows, and token management. ## Quick diagnostics [Section titled “Quick diagnostics”](#quick-diagnostics) Start with these quick checks to identify the issue: ### Check connected account status [Section titled “Check connected account status”](#check-connected-account-status) * Python ```python 1 # Get connected account status 2 account = actions.get_connected_account( 3 identifier="user_123", 4 connection_name="gmail" 5 ) 6 7 print(f"Status: {account.status}") 8 print(f"Provider: {account.connection_name}") 9 print(f"Created: {account.created_at}") 10 print(f"Updated: {account.updated_at}") 11 12 # Status values: 13 # - PENDING: User hasn't completed authentication 14 # - ACTIVE: Connection is active and working 15 # - EXPIRED: Tokens expired, refresh may be needed 16 # - REVOKED: User revoked access 17 # - ERROR: Authentication error occurred ``` * Node.js ```javascript 1 // Get connected account status 2 const account = await scalekit.actions.getConnectedAccount({ 3 identifier: 'user_123', 4 connectionName: 'gmail' 5 }); 6 7 console.log(`Status: ${account.status}`); 8 console.log(`Provider: ${account.connectionName}`); 9 console.log(`Created: ${account.createdAt}`); 10 console.log(`Updated: ${account.updatedAt}`); 11 12 // Status values: 13 // - PENDING: User hasn't completed authentication 14 // - ACTIVE: Connection is active and working 15 // - EXPIRED: Tokens expired, refresh may be needed 16 // - REVOKED: User revoked access 17 // - ERROR: Authentication error occurred ``` * Go ```go 1 // Get connected account status 2 account, err := scalekitClient.Actions.GetConnectedAccount( 3 context.Background(), 4 "user_123", 5 "gmail", 6 ) 7 if err != nil { 8 log.Printf("Error getting account: %v", err) 9 return 10 } 11 12 fmt.Printf("Status: %s\n", account.Status) 13 fmt.Printf("Provider: %s\n", account.ConnectionName) 14 fmt.Printf("Created: %s\n", account.CreatedAt) 15 fmt.Printf("Updated: %s\n", account.UpdatedAt) ``` * Java ```java 1 // Get connected account status 2 ConnectedAccount account = scalekitClient.actions().getConnectedAccount( 3 "user_123", 4 "gmail" 5 ); 6 7 System.out.println("Status: " + account.getStatus()); 8 System.out.println("Provider: " + account.getConnectionName()); 9 System.out.println("Created: " + account.getCreatedAt()); 10 System.out.println("Updated: " + account.getUpdatedAt()); ``` ### Test tool execution [Section titled “Test tool execution”](#test-tool-execution) Try executing a simple tool to verify the connection: ```python 1 # Test with a simple read operation 2 try: 3 result = actions.execute_tool( 4 identifier="user_123", 5 tool_name='gmail_get_profile', # Simple read-only operation 6 tool_input={} 7 ) 8 print("✓ Connection working:", result) 9 except Exception as e: 10 print("✗ Connection failed:", str(e)) 11 # Error message provides clues about the issue ``` ## Common authentication errors [Section titled “Common authentication errors”](#common-authentication-errors) ### PENDING status - User hasn’t authenticated [Section titled “PENDING status - User hasn’t authenticated”](#pending-status---user-hasnt-authenticated) **Symptom:** Connected account status shows `PENDING` **Cause:** User created the connected account but hasn’t completed OAuth flow **Solution:** 1. Generate a new authorization link 2. Send it to the user via email, notification, or in-app message 3. User clicks link and completes authentication 4. Status changes to `ACTIVE` * Python ```python 1 # Generate authorization link for pending account 2 if account.status == "PENDING": 3 link_response = actions.get_authorization_link( 4 connection_name="gmail", 5 identifier="user_123" 6 ) 7 8 print(f"Send this link to user: {link_response.link}") 9 10 # In production: 11 # - Send email with the link 12 # - Show in-app notification 13 # - Display in user's settings page ``` * Node.js ```javascript 1 // Generate authorization link for pending account 2 if (account.status === 'PENDING') { 3 const linkResponse = await scalekit.actions.getAuthorizationLink({ 4 connectionName: 'gmail', 5 identifier: 'user_123' 6 }); 7 8 console.log(`Send this link to user: ${linkResponse.link}`); 9 10 // In production: 11 // - Send email with the link 12 // - Show in-app notification 13 // - Display in user's settings page 14 } ``` * Go ```go 1 // Generate authorization link for pending account 2 if account.Status == "PENDING" { 3 linkResponse, err := scalekitClient.Actions.GetAuthorizationLink( 4 context.Background(), 5 "gmail", 6 "user_123", 7 ) 8 if err != nil { 9 log.Fatal(err) 10 } 11 12 fmt.Printf("Send this link to user: %s\n", linkResponse.Link) 13 } ``` * Java ```java 1 // Generate authorization link for pending account 2 if ("PENDING".equals(account.getStatus())) { 3 AuthorizationLink linkResponse = scalekitClient.actions().getAuthorizationLink( 4 "gmail", 5 "user_123" 6 ); 7 8 System.out.println("Send this link to user: " + linkResponse.getLink()); 9 } ``` ### EXPIRED status - Tokens need refresh [Section titled “EXPIRED status - Tokens need refresh”](#expired-status---tokens-need-refresh) **Symptom:** Connected account status shows `EXPIRED` **Causes:** * Access token expired and automatic refresh failed * Refresh token became invalid * Provider temporarily unavailable during refresh **Solutions:** **Option 1: Try manual refresh** ```python 1 # Attempt manual token refresh 2 try: 3 account = actions.refresh_connected_account( 4 identifier="user_123", 5 connection_name="gmail" 6 ) 7 if account.status == "ACTIVE": 8 print("✓ Refresh successful") 9 else: 10 print("⚠ Refresh failed, user re-authentication needed") 11 except Exception as e: 12 print(f"✗ Refresh error: {e}") 13 # Proceed to Option 2 ``` **Option 2: Request user re-authentication** ```python 1 # If refresh fails, generate new authorization link 2 link_response = actions.get_authorization_link( 3 connection_name="gmail", 4 identifier="user_123" 5 ) 6 7 # Notify user to re-authenticate 8 print(f"Please re-authorize: {link_response.link}") ``` ### REVOKED status - User revoked access [Section titled “REVOKED status - User revoked access”](#revoked-status---user-revoked-access) **Symptom:** Connected account status shows `REVOKED` **Cause:** User revoked your application’s access through the provider’s settings (e.g., Google Account Settings, Microsoft Account Permissions) **Solution:** User must re-authenticate to restore access ```python 1 # For revoked accounts, only re-authentication works 2 if account.status == "REVOKED": 3 link_response = actions.get_authorization_link( 4 connection_name="gmail", 5 identifier="user_123" 6 ) 7 8 # Explain to user why re-authentication is needed 9 message = """ 10 Your Gmail connection was disconnected. 11 This may have happened if you: 12 - Revoked access in your Google Account settings 13 - Changed your Google password 14 - Enabled 2FA on your Google account 15 16 Please reconnect: {link} 17 """.format(link=link_response.link) 18 19 print(message) ``` Caution When a user revokes access, any pending tool executions will fail. Ensure your application handles `REVOKED` status gracefully and notifies users promptly. ## OAuth flow issues [Section titled “OAuth flow issues”](#oauth-flow-issues) ### Callback errors [Section titled “Callback errors”](#callback-errors) **Symptom:** OAuth redirect fails or returns error **Common errors and solutions:** | Error Code | Meaning | Solution | | --------------------- | --------------------------- | ------------------------------------------------ | | `access_denied` | User cancelled OAuth flow | Normal behavior, offer retry option | | `invalid_request` | Malformed OAuth request | Check OAuth parameters and scopes | | `unauthorized_client` | OAuth client not authorized | Verify OAuth credentials in Scalekit dashboard | | `invalid_scope` | Requested scope not valid | Review and correct requested scopes | | `server_error` | Provider error | Retry after a few minutes, check provider status | **Debugging callback issues:** ```python 1 # In your OAuth callback handler 2 def handle_oauth_callback(request): 3 error = request.args.get('error') 4 error_description = request.args.get('error_description') 5 code = request.args.get('code') 6 state = request.args.get('state') 7 8 if error: 9 # Log the error for debugging 10 print(f"OAuth error: {error}") 11 print(f"Description: {error_description}") 12 13 # Handle specific errors 14 if error == 'access_denied': 15 return "You cancelled the authorization. Please try again." 16 elif error == 'invalid_scope': 17 return "Invalid permissions requested. Please contact support." 18 else: 19 return f"Authorization failed: {error_description}" 20 21 if not code: 22 return "Missing authorization code" 23 24 # Continue with normal flow 25 # Scalekit handles the code exchange automatically 26 return "Authorization successful!" ``` ### Redirect URI mismatch [Section titled “Redirect URI mismatch”](#redirect-uri-mismatch) **Symptom:** Error message about redirect URI mismatch **Cause:** OAuth provider redirect URI doesn’t match configured URI in connection **Solution:** 1. Check the redirect URI in Scalekit dashboard 2. Navigate to **Connections** > Select connection > View **Redirect URI** 3. Copy the exact Scalekit redirect URI 4. Add it to your OAuth application in provider’s console (Google, Microsoft, etc.) 5. Ensure there are no trailing slashes or protocol mismatches (http vs https) ### State parameter validation failure [Section titled “State parameter validation failure”](#state-parameter-validation-failure) **Symptom:** “Invalid state parameter” error **Cause:** State parameter doesn’t match or is missing (CSRF protection) **Solution:** This is handled automatically by Scalekit, but if you encounter this: 1. Ensure cookies are enabled in the browser 2. Check for clock skew between systems 3. Verify user isn’t switching browsers/devices mid-flow 4. Try clearing browser cookies and restarting flow ## Provider-specific issues [Section titled “Provider-specific issues”](#provider-specific-issues) ### Google Workspace [Section titled “Google Workspace”](#google-workspace) **Issue: “Access blocked: Authorization Error”** **Causes:** * App not verified by Google * Using restricted scopes * Domain admin restrictions **Solutions:** * Complete Google’s app verification process * Use less restrictive scopes during development * Contact domain admin to whitelist your app **Issue: “This app isn’t verified”** **Solution:** * Click “Advanced” → “Go to \[Your App] (unsafe)” for testing * Submit app for Google verification for production * Use Scalekit’s shared credentials for quick testing ### Microsoft 365 [Section titled “Microsoft 365”](#microsoft-365) **Issue: “AADSTS65001: User or administrator has not consented”** **Solution:** * Ensure required permissions are configured in Azure AD * Admin consent may be required for certain scopes * Check tenant-specific restrictions **Issue: “AADSTS50020: User account from identity provider does not exist”** **Solution:** * User must have a valid Microsoft 365 account * Check if user’s tenant allows external app access * Verify user’s email domain matches tenant ### Slack [Section titled “Slack”](#slack) **Issue: “OAuth access denied”** **Solution:** * User must have permission to install apps in their Slack workspace * Check workspace app approval settings * Ensure required scopes are not restricted by workspace admin **Issue: “Workspace installation restricted”** **Solution:** * Contact Slack workspace admin * Request app approval if workspace requires it * Use a different workspace for testing ## Tool execution failures [Section titled “Tool execution failures”](#tool-execution-failures) ### Authentication errors during execution [Section titled “Authentication errors during execution”](#authentication-errors-during-execution) **Symptom:** Tool execution fails with authentication error despite `ACTIVE` status **Debugging steps:** ```python 1 # Step 1: Verify account status 2 account = actions.get_connected_account( 3 identifier="user_123", 4 connection_name="gmail" 5 ) 6 print(f"Status: {account.status}") 7 8 # Step 2: Try to refresh tokens 9 try: 10 account = actions.refresh_connected_account( 11 identifier="user_123", 12 connection_name="gmail" 13 ) 14 print("✓ Token refresh successful") 15 except Exception as e: 16 print(f"✗ Token refresh failed: {e}") 17 18 # Step 3: Check granted scopes 19 print(f"Granted scopes: {account.scopes}") 20 # Verify the required scope for your tool is included 21 22 # Step 4: Try a simple read-only tool 23 try: 24 result = actions.execute_tool( 25 identifier="user_123", 26 tool_name='gmail_get_profile', 27 tool_input={} 28 ) 29 print("✓ Read operation successful") 30 except Exception as e: 31 print(f"✗ Read operation failed: {e}") ``` ### Insufficient permissions [Section titled “Insufficient permissions”](#insufficient-permissions) **Symptom:** “Insufficient permissions” or “Forbidden” error **Cause:** Required scope not granted during authentication **Solution:** 1. Check currently granted scopes 2. Determine required scopes for the tool 3. Request additional scopes by having user re-authenticate 4. Update connection scopes if needed ```python 1 # Check if specific scope is granted 2 required_scope = "https://www.googleapis.com/auth/gmail.send" 3 4 account = actions.get_connected_account( 5 identifier="user_123", 6 connection_name="gmail" 7 ) 8 9 if required_scope not in account.scopes: 10 print(f"⚠ Missing required scope: {required_scope}") 11 12 # Generate new authorization link with required scopes 13 link_response = actions.get_authorization_link( 14 connection_name="gmail", 15 identifier="user_123" 16 ) 17 18 print(f"User must re-authorize with additional permissions: {link_response.link}") ``` ## Connection configuration issues [Section titled “Connection configuration issues”](#connection-configuration-issues) ### Invalid OAuth credentials [Section titled “Invalid OAuth credentials”](#invalid-oauth-credentials) **Symptom:** “Invalid client” or “Client authentication failed” **Cause:** OAuth client ID or client secret incorrect or revoked **Solution:** 1. Navigate to Scalekit dashboard → **Connections** 2. Select the affected connection 3. Verify OAuth credentials match provider’s console 4. If using BYOC (Bring Your Own Credentials), double-check: * Client ID is correct * Client Secret hasn’t been regenerated * OAuth application is active in provider’s console 5. Update credentials if needed 6. Test connection with a new connected account ### Missing or incorrect scopes [Section titled “Missing or incorrect scopes”](#missing-or-incorrect-scopes) **Symptom:** Authorization succeeds but tool execution fails **Cause:** Connection configured with insufficient scopes **Solution:** ```python 1 # Check connection configuration in dashboard 2 # Ensure these scopes are configured: 3 4 # For Gmail: 5 # - https://www.googleapis.com/auth/gmail.readonly (read emails) 6 # - https://www.googleapis.com/auth/gmail.send (send emails) 7 # - https://www.googleapis.com/auth/gmail.modify (modify emails) 8 9 # For Google Calendar: 10 # - https://www.googleapis.com/auth/calendar.readonly (read calendar) 11 # - https://www.googleapis.com/auth/calendar.events (manage events) 12 13 # After updating scopes in connection, existing users must re-authenticate ``` ## Rate limiting and quota issues [Section titled “Rate limiting and quota issues”](#rate-limiting-and-quota-issues) ### Provider rate limits exceeded [Section titled “Provider rate limits exceeded”](#provider-rate-limits-exceeded) **Symptom:** “Rate limit exceeded” or “Quota exceeded” errors **Causes:** * Too many requests in short time period * Shared quota limits (when using Scalekit’s shared credentials) * Provider-specific rate limits **Solutions:** **Immediate:** * Implement exponential backoff and retry logic * Reduce request frequency * Batch operations where possible **Long-term:** * Use Bring Your Own Credentials for dedicated quotas * Implement request queuing * Cache frequently accessed data ```python 1 import time 2 from typing import Any, Dict 3 4 def execute_tool_with_retry( 5 identifier: str, 6 tool_name: str, 7 tool_input: Dict[str, Any], 8 max_retries: int = 3 9 ): 10 """Execute tool with exponential backoff retry logic""" 11 for attempt in range(max_retries): 12 try: 13 result = actions.execute_tool( 14 identifier=identifier, 15 tool_name=tool_name, 16 tool_input=tool_input 17 ) 18 return result 19 except Exception as e: 20 if "rate limit" in str(e).lower() and attempt < max_retries - 1: 21 # Exponential backoff: 1s, 2s, 4s 22 wait_time = 2 ** attempt 23 print(f"Rate limited, retrying in {wait_time}s...") 24 time.sleep(wait_time) 25 else: 26 raise 27 28 # Usage 29 result = execute_tool_with_retry( 30 identifier="user_123", 31 tool_name="gmail_send_email", 32 tool_input={"to": "user@example.com", "subject": "Test", "body": "Hello"} 33 ) ``` ## Network and connectivity issues [Section titled “Network and connectivity issues”](#network-and-connectivity-issues) ### Timeout errors [Section titled “Timeout errors”](#timeout-errors) **Symptom:** Requests timeout or take too long **Causes:** * Network connectivity issues * Provider API slow response * Large data transfers **Solutions:** * Increase timeout settings in your application * Implement async processing for slow operations * Check provider status page for known issues * Retry with exponential backoff ### SSL/TLS errors [Section titled “SSL/TLS errors”](#ssltls-errors) **Symptom:** SSL certificate verification failures **Causes:** * Outdated SSL certificates * Corporate proxy/firewall issues * System clock skew **Solutions:** * Update system CA certificates * Configure proxy settings if behind corporate firewall * Verify system clock is synchronized * Check firewall allows connections to Scalekit and provider domains ## Debugging tools and techniques [Section titled “Debugging tools and techniques”](#debugging-tools-and-techniques) ### Enable detailed logging [Section titled “Enable detailed logging”](#enable-detailed-logging) * Python ```python 1 import logging 2 3 # Enable debug logging for Scalekit SDK 4 logging.basicConfig(level=logging.DEBUG) 5 logger = logging.getLogger('scalekit') 6 logger.setLevel(logging.DEBUG) 7 8 # Now all API requests/responses will be logged 9 result = actions.execute_tool(...) ``` * Node.js ```javascript 1 // Enable debug mode in SDK initialization 2 const scalekit = new ScalekitClient({ 3 clientId: process.env.SCALEKIT_CLIENT_ID, 4 clientSecret: process.env.SCALEKIT_CLIENT_SECRET, 5 envUrl: process.env.SCALEKIT_ENV_URL, 6 debug: true // Enable detailed logging 7 }); ``` * Go ```go 1 // Enable debug logging 2 scalekitClient := scalekit.NewScalekitClient( 3 scalekit.WithClientID(os.Getenv("SCALEKIT_CLIENT_ID")), 4 scalekit.WithClientSecret(os.Getenv("SCALEKIT_CLIENT_SECRET")), 5 scalekit.WithEnvURL(os.Getenv("SCALEKIT_ENV_URL")), 6 scalekit.WithDebug(true), // Enable debug mode 7 ) ``` * Java ```java 1 // Enable debug logging 2 ScalekitClient scalekitClient = new ScalekitClient.Builder() 3 .clientId(System.getenv("SCALEKIT_CLIENT_ID")) 4 .clientSecret(System.getenv("SCALEKIT_CLIENT_SECRET")) 5 .envUrl(System.getenv("SCALEKIT_ENV_URL")) 6 .debug(true) // Enable debug mode 7 .build(); ``` ### Check Scalekit dashboard [Section titled “Check Scalekit dashboard”](#check-scalekit-dashboard) The Scalekit dashboard provides detailed information: 1. Navigate to **AgentKit** > **Connected Accounts** 2. Find the affected connected account 3. View: * Current status and last updated time * Authentication events and errors * Token refresh history * Tool execution logs * Error messages and stack traces ### Test with curl [Section titled “Test with curl”](#test-with-curl) Test authentication directly with curl to isolate issues: ```bash 1 # Get connected account status 2 curl -X GET "https://api.scalekit.com/v1/connect/accounts/{account_id}" \ 3 -H "Authorization: Bearer YOUR_API_TOKEN" 4 5 # Refresh tokens 6 curl -X POST "https://api.scalekit.com/v1/connect/accounts/{account_id}/refresh" \ 7 -H "Authorization: Bearer YOUR_API_TOKEN" 8 9 # Execute tool 10 curl -X POST "https://api.scalekit.com/v1/connect/tools/execute" \ 11 -H "Authorization: Bearer YOUR_API_TOKEN" \ 12 -H "Content-Type: application/json" \ 13 -d '{ 14 "connected_account_id": "account_123", 15 "tool_name": "gmail_get_profile", 16 "tool_input": {} 17 }' ``` ## Getting help [Section titled “Getting help”](#getting-help) ### Information to provide [Section titled “Information to provide”](#information-to-provide) When contacting support, include: * **Connected Account ID**: Found in dashboard or API response * **Connection Name**: Which provider (gmail, slack, etc.) * **Error Messages**: Complete error text and stack traces * **Timestamp**: When the error occurred * **Steps to Reproduce**: What actions led to the error * **Expected Behavior**: What should have happened * **Environment**: Development, staging, or production ### Support channels [Section titled “Support channels”](#support-channels) * **Documentation**: Check related guides in docs * **Dashboard Logs**: Review logs in Scalekit dashboard * **Support Portal**: Submit ticket with details above * **Developer Community**: Ask questions in community forums * **Email Support**:  for critical issues ## Next steps [Section titled “Next steps”](#next-steps) * [Scopes and Permissions](/agentkit/authentication/scopes-permissions) - Managing OAuth scopes * [Multi-Provider Authentication](/agentkit/authentication/multi-provider) - Managing multiple connections

---
# DOCUMENT BOUNDARY
---

# Create your own connector

> Choose an auth type, build the connector payload, and create or manage custom connectors in Scalekit.

This page covers everything you need to create a custom connector: building the connector payload and managing it with the API. ## Create a connector [Section titled “Create a connector”](#create-a-connector) Share the connector name, Scalekit credentials, and API docs. The skill infers the auth type, generates the payload, and walks you through create, update, and promotion to Production. Always review the final payload before approving. To manage connectors directly via API, use the payloads below. Below are example payloads for API and MCP connectors across all supported auth patterns. * API Connector * OAuth ```json 1 { 2 "display_name": "My Asana", 3 "description": "Connect to Asana. Manage tasks, projects, teams, and workflow automation", 4 "auth_patterns": [ 5 { 6 "type": "OAUTH", 7 "display_name": "OAuth 2.0", 8 "description": "Authenticate with Asana using OAuth 2.0 for comprehensive project management", 9 "fields": [], 10 "oauth_config": { 11 "authorize_uri": "https://app.asana.com/-/oauth_authorize", 12 "token_uri": "https://app.asana.com/-/oauth_token", 13 "user_info_uri": "https://app.asana.com/api/1.0/users/me", 14 "available_scopes": [ 15 { 16 "scope": "profile", 17 "display_name": "Profile", 18 "description": "Access user profile information", 19 "required": true 20 }, 21 { 22 "scope": "email", 23 "display_name": "Email", 24 "description": "Access user email address", 25 "required": true 26 } 27 ] 28 } 29 } 30 ], 31 "proxy_url": "https://app.asana.com/api", 32 "proxy_enabled": true 33 } ``` * Bearer ```json 1 { 2 "display_name": "My Bearer Token Provider", 3 "description": "Connect to an API that accepts a static bearer token", 4 "auth_patterns": [ 5 { 6 "type": "BEARER", 7 "display_name": "Bearer Token", 8 "description": "Authenticate with a static bearer token", 9 "fields": [ 10 { 11 "field_name": "token", 12 "label": "Bearer Token", 13 "input_type": "password", 14 "hint": "Your long-lived bearer token", 15 "required": true 16 } 17 ] 18 } 19 ], 20 "proxy_url": "https://api.example.com", 21 "proxy_enabled": true 22 } ``` * Basic ```json 1 { 2 "display_name": "My Freshdesk", 3 "description": "Connect to Freshdesk. Manage tickets, contacts, companies, and customer support workflows", 4 "auth_patterns": [ 5 { 6 "type": "BASIC", 7 "display_name": "Basic Auth", 8 "description": "Authenticate with Freshdesk using Basic Auth with username and password for comprehensive helpdesk management", 9 "fields": [ 10 { 11 "field_name": "domain", 12 "label": "Freshdesk Domain", 13 "input_type": "text", 14 "hint": "Your Freshdesk domain (e.g., yourcompany.freshdesk.com)", 15 "required": true 16 }, 17 { 18 "field_name": "username", 19 "label": "API Key", 20 "input_type": "text", 21 "hint": "Your Freshdesk API Key", 22 "required": true 23 } 24 ] 25 } 26 ], 27 "proxy_url": "https://{{domain}}/api", 28 "proxy_enabled": true 29 } ``` * API Key ```json 1 { 2 "display_name": "My Attention", 3 "description": "Connect to Attention for AI insights, conversations, teams, and workflows", 4 "auth_patterns": [ 5 { 6 "type": "API_KEY", 7 "display_name": "API Key", 8 "description": "Authenticate with Attention using an API Key", 9 "fields": [ 10 { 11 "field_name": "api_key", 12 "label": "Integration Token", 13 "input_type": "password", 14 "hint": "Your Attention API Key", 15 "required": true 16 } 17 ] 18 } 19 ], 20 "proxy_url": "https://api.attention.tech", 21 "proxy_enabled": true 22 } ``` * MCP Connector ```json 1 { 2 "display_name": "My Asana", 3 "description": "Connect to Asana. Manage tasks, projects, teams, and workflow automation", 4 "auth_patterns": [ 5 { 6 "type": "OAUTH", 7 "display_name": "OAuth 2.0", 8 "description": "Authenticate with Asana using OAuth 2.0 for comprehensive project management", 9 "fields": [], 10 "oauth_config": { 11 "authorize_uri": "https://app.asana.com/-/oauth_authorize", 12 "token_uri": "https://app.asana.com/-/oauth_token", 13 "user_info_uri": "https://app.asana.com/api/1.0/users/me", 14 "available_scopes": [ 15 { 16 "scope": "profile", 17 "display_name": "Profile", 18 "description": "Access user profile information", 19 "required": true 20 }, 21 { 22 "scope": "email", 23 "display_name": "Email", 24 "description": "Access user email address", 25 "required": true 26 } 27 ] 28 } 29 } 30 ], 31 "proxy_url": "https://app.asana.com/api", 32 "proxy_enabled": true 33 } ``` * OAuth ```json 1 { 2 "display_name": "My Bearer Token Provider", 3 "description": "Connect to an API that accepts a static bearer token", 4 "auth_patterns": [ 5 { 6 "type": "BEARER", 7 "display_name": "Bearer Token", 8 "description": "Authenticate with a static bearer token", 9 "fields": [ 10 { 11 "field_name": "token", 12 "label": "Bearer Token", 13 "input_type": "password", 14 "hint": "Your long-lived bearer token", 15 "required": true 16 } 17 ] 18 } 19 ], 20 "proxy_url": "https://api.example.com", 21 "proxy_enabled": true 22 } ``` * Bearer ```json 1 { 2 "display_name": "My Freshdesk", 3 "description": "Connect to Freshdesk. Manage tickets, contacts, companies, and customer support workflows", 4 "auth_patterns": [ 5 { 6 "type": "BASIC", 7 "display_name": "Basic Auth", 8 "description": "Authenticate with Freshdesk using Basic Auth with username and password for comprehensive helpdesk management", 9 "fields": [ 10 { 11 "field_name": "domain", 12 "label": "Freshdesk Domain", 13 "input_type": "text", 14 "hint": "Your Freshdesk domain (e.g., yourcompany.freshdesk.com)", 15 "required": true 16 }, 17 { 18 "field_name": "username", 19 "label": "API Key", 20 "input_type": "text", 21 "hint": "Your Freshdesk API Key", 22 "required": true 23 } 24 ] 25 } 26 ], 27 "proxy_url": "https://{{domain}}/api", 28 "proxy_enabled": true 29 } ``` * Basic ```json 1 { 2 "display_name": "My Attention", 3 "description": "Connect to Attention for AI insights, conversations, teams, and workflows", 4 "auth_patterns": [ 5 { 6 "type": "API_KEY", 7 "display_name": "API Key", 8 "description": "Authenticate with Attention using an API Key", 9 "fields": [ 10 { 11 "field_name": "api_key", 12 "label": "Integration Token", 13 "input_type": "password", 14 "hint": "Your Attention API Key", 15 "required": true 16 } 17 ] 18 } 19 ], 20 "proxy_url": "https://api.attention.tech", 21 "proxy_enabled": true 22 } ``` * API Key * OAuth ```json 1 { 2 "display_name": "Github MCP", 3 "description": "Connect to Github MCP", 4 "auth_patterns": [ 5 { 6 "description": "Authenticate with Github MCP using browser OAuth.", 7 "display_name": "OAuth 2.1/DCR", 8 "fields": [], 9 "is_mcp": true, 10 "oauth_config": { 11 "pkce_enabled": true 12 }, 13 "type": "OAUTH" 14 } 15 ], 16 "proxy_url": "https://api.githubcopilot.com/mcp/", 17 "proxy_enabled": true 18 } ``` * Bearer ```json 1 { 2 "display_name": "Apify MCP", 3 "description": "Connect to Apify MCP to run web scraping, browser automation, and data extraction Actors directly from your AI workflows.", 4 "auth_patterns": [ 5 { 6 "description": "Authenticate with Apify using your API Token.", 7 "display_name": "Apify Token", 8 "fields": [ 9 { 10 "field_name": "token", 11 "hint": "Your Apify API Token", 12 "input_type": "password", 13 "label": "Apify Token", 14 "required": true 15 } 16 ], 17 "is_mcp": true, 18 "type": "BEARER" 19 } 20 ], 21 "proxy_url": "https://mcp.apify.com", 22 "proxy_enabled": true 23 } ``` * Basic ```json 1 { 2 "display_name": "My Internal MCP", 3 "description": "Connect to an internal MCP server that authenticates with a username and password", 4 "auth_patterns": [ 5 { 6 "type": "BASIC", 7 "display_name": "Basic Auth", 8 "description": "Authenticate with a username and password", 9 "is_mcp": true, 10 "fields": [ 11 { 12 "field_name": "username", 13 "label": "Username", 14 "input_type": "text", 15 "hint": "Your username", 16 "required": true 17 }, 18 { 19 "field_name": "password", 20 "label": "Password", 21 "input_type": "password", 22 "hint": "Your password", 23 "required": true 24 } 25 ] 26 } 27 ], 28 "proxy_url": "https://mcp.internal.example.com", 29 "proxy_enabled": true 30 } ``` * API Key ```json 1 { 2 "display_name": "My API Key MCP", 3 "description": "Connect to an MCP server that authenticates with a static API key", 4 "auth_patterns": [ 5 { 6 "type": "API_KEY", 7 "display_name": "API Key", 8 "description": "Authenticate with a static API key", 9 "is_mcp": true, 10 "fields": [ 11 { 12 "field_name": "api_key", 13 "label": "API Key", 14 "input_type": "password", 15 "hint": "Your API key", 16 "required": true 17 } 18 ] 19 } 20 ], 21 "proxy_url": "https://mcp.example.com", 22 "proxy_enabled": true 23 } ``` * OAuth ```json 1 { 2 "display_name": "Github MCP", 3 "description": "Connect to Github MCP", 4 "auth_patterns": [ 5 { 6 "description": "Authenticate with Github MCP using browser OAuth.", 7 "display_name": "OAuth 2.1/DCR", 8 "fields": [], 9 "is_mcp": true, 10 "oauth_config": { 11 "pkce_enabled": true 12 }, 13 "type": "OAUTH" 14 } 15 ], 16 "proxy_url": "https://api.githubcopilot.com/mcp/", 17 "proxy_enabled": true 18 } ``` * Bearer ```json 1 { 2 "display_name": "Apify MCP", 3 "description": "Connect to Apify MCP to run web scraping, browser automation, and data extraction Actors directly from your AI workflows.", 4 "auth_patterns": [ 5 { 6 "description": "Authenticate with Apify using your API Token.", 7 "display_name": "Apify Token", 8 "fields": [ 9 { 10 "field_name": "token", 11 "hint": "Your Apify API Token", 12 "input_type": "password", 13 "label": "Apify Token", 14 "required": true 15 } 16 ], 17 "is_mcp": true, 18 "type": "BEARER" 19 } 20 ], 21 "proxy_url": "https://mcp.apify.com", 22 "proxy_enabled": true 23 } ``` * Basic ```json 1 { 2 "display_name": "My Internal MCP", 3 "description": "Connect to an internal MCP server that authenticates with a username and password", 4 "auth_patterns": [ 5 { 6 "type": "BASIC", 7 "display_name": "Basic Auth", 8 "description": "Authenticate with a username and password", 9 "is_mcp": true, 10 "fields": [ 11 { 12 "field_name": "username", 13 "label": "Username", 14 "input_type": "text", 15 "hint": "Your username", 16 "required": true 17 }, 18 { 19 "field_name": "password", 20 "label": "Password", 21 "input_type": "password", 22 "hint": "Your password", 23 "required": true 24 } 25 ] 26 } 27 ], 28 "proxy_url": "https://mcp.internal.example.com", 29 "proxy_enabled": true 30 } ``` * API Key ```json 1 { 2 "display_name": "My API Key MCP", 3 "description": "Connect to an MCP server that authenticates with a static API key", 4 "auth_patterns": [ 5 { 6 "type": "API_KEY", 7 "display_name": "API Key", 8 "description": "Authenticate with a static API key", 9 "is_mcp": true, 10 "fields": [ 11 { 12 "field_name": "api_key", 13 "label": "API Key", 14 "input_type": "password", 15 "hint": "Your API key", 16 "required": true 17 } 18 ] 19 } 20 ], 21 "proxy_url": "https://mcp.example.com", 22 "proxy_enabled": true 23 } ``` **Before submitting, review the final payload carefully:** * `display_name` and `description` * The selected auth `type` * Required `fields` and `account_fields` * OAuth endpoints and scopes, if the connector uses OAuth * `proxy_url` * Whether `is_mcp` is set to `true` for MCP providers Use the payload for your auth type as the request body in the create request: ```bash 1 curl --location "$SCALEKIT_ENVIRONMENT_URL/api/v1/custom-providers" \ 2 --header "Authorization: Bearer $env_access_token" \ 3 --header "Content-Type: application/json" \ 4 --data '{...}' ``` After the connector is created, create a connection in the Scalekit Dashboard and continue with the standard connector flow. ## List connectors [Section titled “List connectors”](#list-connectors) List existing connectors to confirm whether to create a new one or update an existing one. ```bash 1 curl --location "$SCALEKIT_ENVIRONMENT_URL/api/v1/providers?filter.provider_type=CUSTOM&page_size=1000" \ 2 --header "Authorization: Bearer $env_access_token" ``` ## Update a connector [Section titled “Update a connector”](#update-a-connector) Use the [List connectors](#list-connectors) API to get the connector `identifier`, then send the updated payload: ```bash 1 curl --location --request PUT "$SCALEKIT_ENVIRONMENT_URL/api/v1/custom-providers/$PROVIDER_IDENTIFIER" \ 2 --header "Authorization: Bearer $env_access_token" \ 3 --header "Content-Type: application/json" \ 4 --data '{...}' ``` ## Delete a connector [Section titled “Delete a connector”](#delete-a-connector) Use the [List connectors](#list-connectors) API to get the connector `identifier`. If the connector is still in use, remove the related connections or connected accounts first. ```bash 1 curl --location --request DELETE "$SCALEKIT_ENVIRONMENT_URL/api/v1/custom-providers/$PROVIDER_IDENTIFIER" \ 2 --header "Authorization: Bearer $env_access_token" ```

---
# DOCUMENT BOUNDARY
---

# Making tool calls

> Make tool calls using a REST API connector via Tool Proxy, or discover and execute tools from a custom MCP connector.

Use this page to make tool calls after the connector, connection, and connected account are set up. The call method depends on the connector type: * **REST API connectors** — use `actions.request()` to proxy HTTP calls through Tool Proxy * **MCP connectors** — use `list_scoped_tools` to discover available tools, then `execute_tool` to call them Both types use the same connection, connected account, and user authorization model. ## Prerequisites [Section titled “Prerequisites”](#prerequisites) Make sure: * The connector exists and is configured with the right [auth pattern](/agentkit/bring-your-own-connector/create-connector) * A [connection](/agentkit/connections) is configured for the connector * The [connected account](/agentkit/connected-accounts) exists * The user has completed [authorization](/agentkit/tools/authorize) Create a connection for your connector in the Scalekit Dashboard: ![Connections page showing a custom connector connection alongside built-in connectors](/.netlify/images?url=_astro%2Fcustom-provider-connection.CmpN35cw.png\&w=2604\&h=762\&dpl=6a01bf5aba8408000850fe26) After the user completes authorization, the connected account appears in the Connected Accounts tab: ![Connected Accounts tab showing an authenticated account for a custom connector](/.netlify/images?url=_astro%2Fcustom-provider-connected-account.CNBQ7XLh.png\&w=2610\&h=624\&dpl=6a01bf5aba8408000850fe26) ## REST API proxy calls [Section titled “REST API proxy calls”](#rest-api-proxy-calls) In the request examples below, `path` is relative to the connector `proxy_url`. `connectionName` must match the connection you created, and `identifier` must match the connected account you want to use for the request. * Node.js ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node'; 2 import 'dotenv/config'; 3 4 const connectionName = 'your-provider-connection'; // get your connection name from connection configurations 5 const identifier = 'user_123'; // your unique user identifier 6 7 // Get your credentials from app.scalekit.com → Developers → Settings → API Credentials 8 const scalekit = new ScalekitClient( 9 process.env.SCALEKIT_ENV_URL, 10 process.env.SCALEKIT_CLIENT_ID, 11 process.env.SCALEKIT_CLIENT_SECRET 12 ); 13 const actions = scalekit.actions; 14 15 // Authenticate the user 16 const { link } = await actions.getAuthorizationLink({ 17 connectionName, 18 identifier, 19 }); 20 console.log('Authorize connector:', link); 21 process.stdout.write('Press Enter after authorizing...'); 22 await new Promise(r => process.stdin.once('data', r)); 23 24 // Make a request via Scalekit proxy 25 const result = await actions.request({ 26 connectionName, 27 identifier, 28 path: '/v1/customers', 29 method: 'GET', 30 }); 31 console.log(result); ``` * Python ```python 1 import scalekit.client, os 2 from dotenv import load_dotenv 3 load_dotenv() 4 5 connection_name = "your-provider-connection" # get your connection name from connection configurations 6 identifier = "user_123" # your unique user identifier 7 8 # Get your credentials from app.scalekit.com → Developers → Settings → API Credentials 9 scalekit_client = scalekit.client.ScalekitClient( 10 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 11 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 12 env_url=os.getenv("SCALEKIT_ENV_URL"), 13 ) 14 actions = scalekit_client.actions 15 16 # Authenticate the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier 20 ) 21 # present this link to your user for authorization, or click it yourself for testing 22 print("Authorize connector:", link_response.link) 23 input("Press Enter after authorizing...") 24 25 # Make a request via Scalekit proxy 26 result = actions.request( 27 connection_name=connection_name, 28 identifier=identifier, 29 path="/v1/customers", 30 method="GET" 31 ) 32 print(result) ``` The request shape stays the same regardless of auth type — the connector definition controls how Scalekit authenticates the call. ## MCP tool calling [Section titled “MCP tool calling”](#mcp-tool-calling) MCP connectors expose tools from the upstream MCP server. Discover the available tools, then execute them by name. Call `execute_tool` with the connection name, identifier, and any tool-specific input. * Node.js ```typescript 1 const actions = scalekit.actions; 2 3 const result = await actions.executeTool({ 4 toolName: 'tool_name_from_discovery', // replace with a name from list_scoped_tools 5 connector: 'your-mcp-connection', 6 identifier: 'user_123', 7 toolInput: { key: 'value' }, // replace with the tool's required input 8 }); 9 console.log(result); ``` * Python ```python 1 actions = scalekit_client.actions 2 3 result = actions.execute_tool( 4 tool_name="tool_name_from_discovery", # replace with a name from list_scoped_tools 5 connection_name="your-mcp-connection", 6 identifier="user_123", 7 tool_input={"key": "value"}, # replace with the tool's required input 8 ) 9 print(result) ```

---
# DOCUMENT BOUNDARY
---

# Code samples

> Code samples of AI agents using Scalekit along with LangChain, Google ADK, and direct integrations

### [Connect LangChain agents to Gmail](https://github.com/scalekit-inc/sample-langchain-agent) [Securely connect a LangChain agent to Gmail using Scalekit for authentication. Python example for tool authorization.](https://github.com/scalekit-inc/sample-langchain-agent) ### [Connect Google GenAI agents to Gmail](https://github.com/scalekit-inc/google-adk-agent-example) [Build a Google ADK agent that securely accesses Gmail tools. Python example demonstrating Scalekit auth integration.](https://github.com/scalekit-inc/google-adk-agent-example) ### [Connect agents to Slack tools](https://github.com/scalekit-inc/python-connect-demos/tree/main/direct) [Authorize Python agents to use Slack tools with Scalekit. Direct integration example for secure tool access.](https://github.com/scalekit-inc/python-connect-demos/tree/main/direct) ### [Browse all agent auth examples](https://github.com/scalekit-developers/agent-auth-examples) [A curated collection of working examples showing how to build agents that authenticate and access tools using Scalekit.](https://github.com/scalekit-developers/agent-auth-examples)

---
# DOCUMENT BOUNDARY
---

# Manage connected accounts

> Check status, list, delete, and update credentials for connected accounts across all connector auth types.

A **connected account** is the per-user record that holds a user’s credentials and tracks their authorization state for a specific connection. Scalekit creates one automatically when a user completes authentication. ## Account states [Section titled “Account states”](#account-states) | State | Meaning | | --------- | -------------------------------------------------------------- | | `PENDING` | User hasn’t completed authentication | | `ACTIVE` | Credentials valid, ready for tool calls | | `EXPIRED` | Credentials expired or invalidated, re-authentication required | | `REVOKED` | User revoked access or credentials were invalidated | | `ERROR` | Authentication or configuration error | ## Check account status [Section titled “Check account status”](#check-account-status) Use `get_or_create_connected_account` as the safe default when a user may be connecting for the first time. Use `get_connected_account` only when you know the account already exists and you need to inspect or return its stored auth details. * Python ```python 1 response = actions.get_or_create_connected_account( 2 connection_name="gmail", 3 identifier="user_123" 4 ) 5 connected_account = response.connected_account 6 print(f"Status: {connected_account.status}") ``` * Node.js ```typescript 1 const response = await actions.getOrCreateConnectedAccount({ 2 connectionName: 'gmail', 3 identifier: 'user_123', 4 }); 5 6 console.log('Status:', response.connectedAccount?.status); ``` ## Handle inactive accounts [Section titled “Handle inactive accounts”](#handle-inactive-accounts) When a connected account isn’t `ACTIVE`, generate a new authorization link and send it to the user. The link opens a **Hosted Page**, a Scalekit-hosted UI that adapts automatically based on the connection’s auth type: * **OAuth connectors**: presents the provider’s OAuth consent screen * **API key, basic auth, or other connectors**: presents a form to collect the required credentials Your code is the same regardless of connector type. Scalekit determines the right flow based on the connection configuration. * Python ```python 1 if connected_account.status != "ACTIVE": 2 link_response = actions.get_authorization_link( 3 connection_name="gmail", 4 identifier="user_123" 5 ) 6 # Redirect or send link_response.link to the user ``` * Node.js ```typescript 1 import { ConnectorStatus } from '@scalekit-sdk/node/lib/pkg/grpc/scalekit/v1/connected_accounts/connected_accounts_pb'; 2 3 if (connectedAccount?.status !== ConnectorStatus.ACTIVE) { 4 const linkResponse = await actions.getAuthorizationLink({ 5 connectionName: 'gmail', 6 identifier: 'user_123', 7 }); 8 // Redirect or send linkResponse.link to the user 9 } ``` ## List connected accounts [Section titled “List connected accounts”](#list-connected-accounts) ```typescript 1 const listResponse = await actions.listConnectedAccounts({ 2 connectionName: 'gmail', 3 }); 4 console.log('Connected accounts:', listResponse); ``` ## Delete a connected account [Section titled “Delete a connected account”](#delete-a-connected-account) Deleting a connected account removes the user’s credentials and authorization state. The user must re-authenticate to reconnect. ```typescript 1 await actions.deleteConnectedAccount({ 2 connectionName: 'gmail', 3 identifier: 'user_123', 4 }); ``` ## Update OAuth scopes [Section titled “Update OAuth scopes”](#update-oauth-scopes) Scopes apply to OAuth connectors only. For non-OAuth connectors (API key, basic auth, and similar), generate a new authorization link and the hosted page will collect updated credentials. To request additional OAuth scopes from an existing connected account: 1. Update the connection’s scopes in **AgentKit** > **Connections** > **Edit**. 2. Generate a new authorization link for the user. 3. The user completes the OAuth consent screen, approving the updated scopes. 4. Scalekit updates the connected account with the new token set.

---
# DOCUMENT BOUNDARY
---

# Configure a connection

> Set up a connection in the Scalekit Dashboard to authorize your agent to use a third-party connector on behalf of your users.

A **connection** is a configuration you create once in the Scalekit Dashboard. It holds everything Scalekit needs to interact with a connector’s API: OAuth app credentials, scopes, redirect URIs, and so on. One connection serves all your users. Users don’t configure connections. When a user authenticates, Scalekit creates a **connected account**, the per-user record that links their identity to a connection and holds their tokens. ## What the connection form asks for [Section titled “What the connection form asks for”](#what-the-connection-form-asks-for) The connection form adapts to what the connector requires. Two things determine how much you need to configure: * **OAuth-based connectors** require the most setup. You register an OAuth app with the provider, then enter those credentials into Scalekit. * **Non-OAuth connectors** (API key, basic auth, key pairs, and similar) require minimal developer setup (usually just a name). The user provides their own credentials when they create their connected account. The sections below walk through both patterns. ## Set up an OAuth connection [Section titled “Set up an OAuth connection”](#set-up-an-oauth-connection) OAuth connections require you to create an OAuth app with the provider and link it to Scalekit. Scalekit provides the Redirect URI; you bring the Client ID and Client Secret. 1. ### Open the connection form [Section titled “Open the connection form”](#open-the-connection-form) In the Scalekit Dashboard, go to **AgentKit** > **Connections** and click **Add connection**. Select the connector you want to configure. The form shows the fields that connector requires. 2. ### Copy the redirect URI [Section titled “Copy the redirect URI”](#copy-the-redirect-uri) Scalekit generates a **Redirect URI** for this connection. Copy it; you’ll need it in the next step. This URI is where the provider sends the user after they complete the OAuth consent screen. Scalekit handles the callback automatically. 3. ### Register your OAuth app with the provider [Section titled “Register your OAuth app with the provider”](#register-your-oauth-app-with-the-provider) In the provider’s developer console (GitHub, Salesforce, Google, etc.), create an OAuth app and add Scalekit’s Redirect URI to the list of authorized redirect URIs. The provider will give you a **Client ID** and **Client Secret** after registration. Redirect URI must match exactly The URI in the provider’s console must match what Scalekit shows character-for-character, including trailing slashes. A mismatch causes the OAuth flow to fail with a redirect\_uri\_mismatch error. 4. ### Enter your credentials [Section titled “Enter your credentials”](#enter-your-credentials) Back in the Scalekit Dashboard, enter the **Client ID** and **Client Secret** from the provider. 5. ### Configure scopes [Section titled “Configure scopes”](#configure-scopes) Select the scopes your agent needs. Scopes define what your agent can do on the user’s behalf: for example, `read:email` or `repo`. 6. ### Save the connection [Section titled “Save the connection”](#save-the-connection) Click **Save**. The connection is now active and ready for connected accounts to be created against it. ## Set up a non-OAuth connection [Section titled “Set up a non-OAuth connection”](#set-up-a-non-oauth-connection) For connectors that use API keys, basic auth, key pairs, or similar, the connection form asks for very little. In many cases, you only need to give the connection a name. The user provides their own credentials (their API key, account details, or private key) when they create a connected account. Scalekit collects those credentials through the connected account form and stores them securely. 1. Go to **AgentKit** > **Connections** and click **Add connection** 2. Select the connector 3. Enter a **Connection name**: this identifies the connection in the dashboard and in your code 4. Click **Save** When a connected account is created for this connection, Scalekit presents the user with a form that collects the credentials their specific account requires. ## Create multiple connections for the same connector [Section titled “Create multiple connections for the same connector”](#create-multiple-connections-for-the-same-connector) You can create more than one connection for the same connector. This is useful when: * Different groups of users need different scopes * You want to maintain separate OAuth apps for staging and production * You’re integrating with multiple instances of the same service (for example, two different Salesforce orgs) Each connection has its own name, which you use to identify it in API calls and in the dashboard.

---
# DOCUMENT BOUNDARY
---

# Configure an MCP server

> Define which connectors and tools your MCP server exposes by creating an MCP config, a reusable template Scalekit uses to generate per-user MCP URLs.

Suppose you ship an agent that reads a user’s email and creates calendar events from chat. You want an **MCP client** (for example LangChain or Claude Desktop) to call those tools **without** storing users’ OAuth tokens in the MCP client or in browser code, and **without** writing a custom tool-calling loop in your app. Scalekit keeps tokens server-side. An **MCP config** is the single template that lists which [connections](/agentkit/connections/) (for example Gmail and Google Calendar) and which tools appear on the MCP server. This page shows how to create that config. [Generate user MCP URLs](/agentkit/mcp/generate-user-urls/) and [Connect an MCP client](/agentkit/mcp/connect-mcp-client/) cover the steps that follow. | Use MCP when | Use the SDK when | | --------------------------------------------------------------- | ------------------------------------------------- | | You want any MCP-compatible framework to connect | You need fine-grained control over tool execution | | You’re exposing tools to external agents or Claude Desktop | You’re building a custom agent loop | | You want to expose different tool sets to different agent roles | You need to mix Scalekit tools with custom logic | The SDK approach gives your code direct control: you call `execute_tool`, manage the response, and drive the agent loop. The MCP approach inverts this: you generate a pre-authenticated URL per user and hand it to any MCP-compatible agent or framework. The agent discovers available tools itself and executes them through the MCP protocol. Your application code doesn’t manage the loop. ## How it works [Section titled “How it works”](#how-it-works) Two objects are central to this model: | Object | What it is | Created | | ---------------- | ------------------------------------------------------------------------ | ----------------- | | **MCP config** | A reusable template that defines which connections and tools are exposed | Once, by your app | | **MCP instance** | A per-user instantiation of a config, with its own URL | Once per user | You create a config once. For each user, Scalekit generates a unique, pre-authenticated URL from that config. The agent connects to the URL. Scalekit routes tool calls using the user’s authorized credentials. ### One-time setup [Section titled “One-time setup”](#one-time-setup) Declare the MCP config once with `create_config`: which connections and tools appear on the server. ### Per-user [Section titled “Per-user”](#per-user) Call `ensure_instance` for each user. They authorize OAuth for each connection (`auth link`) until every connection is active. You receive a pre-authenticated MCP URL for that user. ### Runtime [Section titled “Runtime”](#runtime) Point your MCP client at that URL (the diagram labels it **AI Agent (MCP URL)**). Tool calls flow to the providers Scalekit proxies using that user’s tokens. ## Prerequisites [Section titled “Prerequisites”](#prerequisites) Before creating a config, configure the connections you want to expose. Each `connection_name` in the config must already exist in **AgentKit** > **Connections**. See [Configure a connection](/agentkit/connections/) if you haven’t set these up yet. ## Create an MCP config [Section titled “Create an MCP config”](#create-an-mcp-config) An MCP config declares which connections and tools your server exposes. Create it once (not once per user). ```python 1 import os 2 import scalekit.client 3 from scalekit.actions.types import McpConfigConnectionToolMapping 4 5 scalekit_client = scalekit.client.ScalekitClient( 6 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 7 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 8 env_url=os.getenv("SCALEKIT_ENV_URL"), 9 ) 10 actions = scalekit_client.actions 11 12 cfg_response = actions.mcp.create_config( 13 name="email-calendar-assistant", 14 description="Reads email and creates calendar reminders", 15 connection_tool_mappings=[ 16 McpConfigConnectionToolMapping( 17 connection_name="MY_GMAIL", 18 tools=["gmail_fetch_mails"], 19 ), 20 McpConfigConnectionToolMapping( 21 connection_name="MY_CALENDAR", 22 tools=["googlecalendar_create_event"], 23 ), 24 ], 25 ) 26 config_name = cfg_response.config.name 27 print("Config created:", config_name) ``` ## Whitelist specific tools [Section titled “Whitelist specific tools”](#whitelist-specific-tools) The `tools` array in each `McpConfigConnectionToolMapping` controls exactly which tools are exposed on the server. To find the available tool names for a connector, call `list_tools` or browse the provider in **AgentKit** > **Catalog**, or open the connection from **AgentKit** > **Connections** and review its tools. ```python 1 # Expose all tools for a connector; omit tools to expose everything 2 McpConfigConnectionToolMapping(connection_name="MY_GMAIL") 3 4 # Expose only specific tools 5 McpConfigConnectionToolMapping( 6 connection_name="MY_GMAIL", 7 tools=["gmail_fetch_mails", "gmail_send_mail"], 8 ) ``` Full working code for all MCP steps is on [GitHub](https://github.com/scalekit-inc/python-connect-demos/tree/main/mcp).

---
# DOCUMENT BOUNDARY
---

# Connect an MCP client

> Pass a Scalekit-generated MCP URL to any MCP-compatible agent, framework, or desktop client. No additional auth configuration required.

The MCP URL you generated is a standard Streamable HTTP MCP endpoint. Any spec-compliant MCP client can connect to it. No additional auth configuration, no SDK calls, no tool schema definitions are required. The client discovers available tools automatically. ## LangChain / LangGraph [Section titled “LangChain / LangGraph”](#langchain--langgraph) ```python 1 import asyncio 2 from langgraph.prebuilt import create_react_agent 3 from langchain_mcp_adapters.client import MultiServerMCPClient 4 5 async def run_agent(mcp_url: str): 6 client = MultiServerMCPClient( 7 { 8 "scalekit": { 9 "transport": "streamable_http", 10 "url": mcp_url, 11 }, 12 } 13 ) 14 tools = await client.get_tools() 15 agent = create_react_agent("openai:gpt-4.1", tools) 16 response = await agent.ainvoke({ 17 "messages": "Get my latest email and create a calendar reminder in the next 15 minutes." 18 }) 19 print(response) 20 21 asyncio.run(run_agent(mcp_url)) ``` Install dependencies: ```sh 1 pip install langgraph>=0.6.5 langchain-mcp-adapters>=0.1.9 openai>=1.53.0 ``` ## Claude Desktop [Section titled “Claude Desktop”](#claude-desktop) Add the MCP URL to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS): ```json 1 { 2 "mcpServers": { 3 "scalekit": { 4 "transport": "streamable-http", 5 "url": "https://your-mcp-url-here" 6 } 7 } 8 } ``` Restart Claude Desktop. The tools appear automatically in the tool menu. ## MCP Inspector [Section titled “MCP Inspector”](#mcp-inspector) Paste the URL directly into [MCP Inspector](https://github.com/modelcontextprotocol/inspector) to explore available tools and make live test calls before wiring up a full agent. ## Any other MCP client [Section titled “Any other MCP client”](#any-other-mcp-client) The URL works with any client that implements the MCP specification using Streamable HTTP transport. Pass the URL, select the transport type, and connect. No credentials to configure. For full end-to-end agent examples with LangChain, Google ADK, and other frameworks, see the [Examples](/agentkit/examples/langchain/) section. Full working code is on [GitHub](https://github.com/scalekit-inc/python-connect-demos/tree/main/mcp).

---
# DOCUMENT BOUNDARY
---

# Generate user MCP URLs

> Create a unique, pre-authenticated MCP URL for each user from an MCP config. The URL is ready to hand to any MCP-compatible agent or framework.

Once you have an MCP config, call `ensure_instance` to get a unique MCP URL for a specific user. Scalekit generates a URL that encodes the user’s identity and authorized connections. The agent connecting to it gets exactly the tools that user is allowed to call. ## Get a per-user MCP URL [Section titled “Get a per-user MCP URL”](#get-a-per-user-mcp-url) `ensure_instance` is idempotent: if an instance already exists for this user and config, Scalekit returns it. Call it on every login without side effects. ```python 1 inst_response = actions.mcp.ensure_instance( 2 config_name=config_name, # from cfg_response.config.name 3 user_identifier="user_123", # your app's unique user ID 4 ) 5 mcp_url = inst_response.instance.url 6 print("MCP URL:", mcp_url) ``` Keep the URL server-side The MCP URL is pre-authenticated. Treat it like a credential. Never expose it to the browser or include it in client-side code. ## Check auth state [Section titled “Check auth state”](#check-auth-state) Before handing the URL to your agent, verify that the user has authorized all connections the config requires. Call `get_instance_auth_state` with `include_auth_links=True` to retrieve auth status and authorization links for any pending connections: ```python 1 auth_state_response = actions.mcp.get_instance_auth_state( 2 instance_id=inst_response.instance.id, 3 include_auth_links=True, 4 ) 5 for conn in auth_state_response.connections: 6 print("Connection:", conn.connection_name) 7 print("Status: ", conn.connected_account_status) 8 print("Auth link: ", conn.authentication_link) ``` Surface the auth links to the user (via your app UI, email, or a Slack message) for any connection that isn’t `ACTIVE`. ## Poll until all connections are authorized [Section titled “Poll until all connections are authorized”](#poll-until-all-connections-are-authorized) Before passing the URL to your agent, poll `get_instance_auth_state` (without `include_auth_links`) until all connections are `ACTIVE`: ```python 1 import time 2 3 def wait_for_auth(instance_id: str, poll_interval: int = 5): 4 while True: 5 state = actions.mcp.get_instance_auth_state(instance_id=instance_id) 6 if all(c.connected_account_status == "ACTIVE" for c in state.connections): 7 print("All connections authorized; MCP URL is ready.") 8 return 9 pending = [c.connection_name for c in state.connections if c.connected_account_status != "ACTIVE"] 10 print(f"Waiting for: {pending}") 11 time.sleep(poll_interval) 12 13 wait_for_auth(inst_response.instance.id) ``` Once all connections are `ACTIVE`, pass `mcp_url` to your agent. See [Connect an MCP client](/agentkit/mcp/connect-mcp-client/) for the next step.

---
# DOCUMENT BOUNDARY
---

# Give your agent tool access via MCP

> Create a per-user MCP server with whitelisted, pre-authenticated tools; then hand your agent a single URL.

When your agent needs to act on behalf of a user (reading their email, creating calendar events), each user must authenticate to each service separately. Managing those credentials in your agent adds complexity and security risk. Scalekit solves this with per-user MCP servers. You define which tools and connections a server exposes, and Scalekit gives you a unique, pre-authenticated URL for each user. Hand that URL to your agent; it calls tools through MCP, Scalekit handles the auth. MCP servers only support Streamable HTTP transport. Testing only: not for production This feature is in beta and intended for testing purposes only. Do not use it in production environments. ## How it works [Section titled “How it works”](#how-it-works) Two objects are central to this model: | Object | What it is | Created | | ---------------- | ------------------------------------------------------------------------ | ----------------- | | **MCP config** | A reusable template that defines which connections and tools are exposed | Once, by your app | | **MCP instance** | A per-user instantiation of a config, with its own URL | Once per user | Your app creates a config once, then calls `ensure_instance` whenever a new user needs access. Scalekit generates a unique URL for that user. When the agent calls tools through that URL, Scalekit routes each call using the user’s pre-authorized credentials. ![Architecture diagram showing how Scalekit MCP works: app creates a config, Scalekit creates per-user instances with unique URLs, users authorize OAuth connections, and the agent connects via MCP URL](/.netlify/images?url=_astro%2Fmcp-tool-access-architecture.Df4E84fg.png\&w=6920\&h=1320\&dpl=6a01bf5aba8408000850fe26) ## Prerequisites [Section titled “Prerequisites”](#prerequisites) Before you start, make sure you have: * **Scalekit API credentials**: go to **Dashboard → Settings** and copy your `environment_url`, `client_id` and `client_secret` * **Gmail and Google Calendar connections configured in Scalekit:** * **Gmail**: Dashboard → **AgentKit** > **Connections** > **Create Connection** → select **Gmail** → set `Connection Name = MY_GMAIL` → Save * **Google Calendar**: Dashboard → **AgentKit** > **Connections** > **Create Connection** → select **Google Calendar** → set `Connection Name = MY_CALENDAR` → Save 1. ## Install the SDK and initialize the client [Section titled “Install the SDK and initialize the client”](#install-the-sdk-and-initialize-the-client) Install the Scalekit Python SDK: ```sh pip install scalekit-sdk-python python-dotenv>=1.0.1 ``` Initialize the client using your environment credentials: ```python import os import scalekit.client from scalekit.actions.models.mcp_config import McpConfigConnectionToolMapping 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"), ) my_mcp = scalekit_client.actions.mcp ``` 2. ## Create an MCP config [Section titled “Create an MCP config”](#create-an-mcp-config) An MCP config is a reusable template. It declares which connections and tools your server exposes. Create it once, not once per user. ```python cfg_response = my_mcp.create_config( name="reminder-manager", description="Summarizes latest email and creates a reminder event", connection_tool_mappings=[ McpConfigConnectionToolMapping( connection_name="MY_GMAIL", tools=["gmail_fetch_mails"], ), McpConfigConnectionToolMapping( connection_name="MY_CALENDAR", tools=["googlecalendar_create_event"], ), ], ) config_name = cfg_response.config.name ``` 3. ## Get a per-user MCP URL [Section titled “Get a per-user MCP URL”](#get-a-per-user-mcp-url) Call `ensure_instance` to get a unique MCP URL for a specific user. If an instance already exists for that user, Scalekit returns it; it’s safe to call on every login. ```python inst_response = my_mcp.ensure_instance( config_name=config_name, user_identifier="john-doe", ) mcp_url = inst_response.instance.url print("MCP URL:", mcp_url) ``` Before the agent can use this URL, the user must authorize each connection. Retrieve the auth links and surface them to the user: ```python auth_state_response = my_mcp.get_instance_auth_state( instance_id=inst_response.instance.id, include_auth_links=True, ) for conn in getattr(auth_state_response, "connections", []): print("Connection:", conn.connection_name, "| Status:", conn.connected_account_status, "| Auth link:", conn.authentication_link) ``` At this point you have a per-user MCP URL. You can pass it to any spec-compliant MCP client: MCP Inspector, Claude Desktop, or an agent framework. The next step shows an example using LangChain. 4. ## Connect an agent (LangChain example) [Section titled “Connect an agent (LangChain example)”](#connect-an-agent-langchain-example) Install the LangChain dependencies: ```sh pip install langgraph>=0.6.5 langchain-mcp-adapters>=0.1.9 openai>=1.53.0 ``` Set your OpenAI API key: ```sh export OPENAI_API_KEY=your-openai-api-key ``` Pass the MCP URL to a LangChain agent. The agent discovers available tools automatically; no additional auth configuration required: ```python import asyncio from langgraph.prebuilt import create_react_agent from langchain_mcp_adapters.client import MultiServerMCPClient async def run_agent(mcp_url: str): client = MultiServerMCPClient( { "reminder_demo": { "transport": "streamable_http", "url": mcp_url, }, } ) tools = await client.get_tools() agent = create_react_agent("openai:gpt-4.1", tools) response = await agent.ainvoke({ "messages": "Get my latest email and create a calendar reminder in the next 15 minutes." }) print(response) asyncio.run(run_agent(mcp_url)) ``` Full working code for all steps above is on [GitHub](https://github.com/scalekit-inc/python-connect-demos/tree/main/mcp). ## Next steps [Section titled “Next steps”](#next-steps) [LangChain integration ](/agentkit/examples/langchain)Use LangChain to build agents that connect to Scalekit MCP servers. [Google ADK integration ](/agentkit/examples/google-adk)Connect Scalekit tools to agents built with Google's Agent Development Kit. [Manage connections ](/agentkit/connections)Learn how to configure and manage connector connections in Scalekit.

---
# DOCUMENT BOUNDARY
---

# OpenClaw skill

> Connect OpenClaw agents to third-party services through Scalekit. Supports LinkedIn, Notion, Slack, Gmail, and 50+ connectors.

Use the Scalekit AgentKit skill for [OpenClaw](https://github.com/scalekit-inc/openclaw-skill) to let your AI agents execute actions on third-party services directly from conversations. Search LinkedIn, read Notion pages, send Slack messages, query Snowflake, and more, all through Scalekit Connect without storing tokens or API keys in your agent. Security considerations for AI agents Scalekit stores tokens and API keys securely with full audit logging. OpenClaw, like all AI agent frameworks, is vulnerable to prompt injection and other agent-level attacks. Follow security best practices to protect your instance. When you ask Claude to interact with a third-party service, the skill: * Finds the configured connector in Scalekit (e.g., [Gmail connection setup](/agentkit/connectors/gmail/)) and identifies which connection to use based on the requested action * Checks if the connection is active. For OAuth connections, it generates a magic link for new authorizations. For API key connections, it provides Dashboard guidance for setup * Retrieves available tools and their parameter schemas for the connector, determining what actions are possible * Calls the right tool with the correct parameters and returns the result to your conversation * If no tool exists for the action, routes the request through Scalekit’s HTTP proxy, making direct API calls on your behalf Your agent never stores tokens or API keys. Scalekit acts as a token vault, managing all OAuth tokens, API keys, and credentials. The skill retrieves only what it needs at runtime, scoped to the requesting user. ## Prerequisites [Section titled “Prerequisites”](#prerequisites) * [OpenClaw](https://openclaw.ai) installed and configured * A Scalekit account with AgentKit enabled: [sign up at app.scalekit.com](https://app.scalekit.com) * `python3` and `uv` available in your PATH ## Get started [Section titled “Get started”](#get-started) 1. ## Install the skill [Section titled “Install the skill”](#install-the-skill) Install the skill from ClawHub: ```bash clawhub install scalekit-agent-auth ``` 2. ## Configure credentials [Section titled “Configure credentials”](#configure-credentials) Add your Scalekit credentials to `.env` in your project root: .env ```bash 1 TOOL_CLIENT_ID=skc_your_client_id # Your Scalekit client ID 2 TOOL_CLIENT_SECRET=your_client_secret # Your Scalekit client secret 3 TOOL_ENV_URL=https://your-env.scalekit.cloud # Your Scalekit environment URL 4 TOOL_IDENTIFIER=your_default_user_identifier # Default user context for tool calls ``` | Parameter | Description | | -------------------- | --------------------------------------------------- | | `TOOL_CLIENT_ID` | Your Scalekit client ID Required | | `TOOL_CLIENT_SECRET` | Your Scalekit client secret Required | | `TOOL_ENV_URL` | Your Scalekit environment URL Required | | `TOOL_IDENTIFIER` | Default user context for all tool calls Recommended | Environment variable security Never commit `.env` files to version control. Add `.env` to your `.gitignore` file to prevent accidental exposure of credentials. 3. ## Usage [Section titled “Usage”](#usage) * Gmail ```txt You: Show me my latest unread emails ``` OpenClaw will automatically: 1. Look up the `GMAIL` connection 2. Verify it’s active (or generate a magic link to authorize if needed) 3. Fetch the `gmail_list_emails` tool schema 4. Return your latest unread emails * Notion ```txt You: Read my Notion page https://notion.so/My-Page-abc123 ``` OpenClaw will: 1. Look up the `NOTION` connection 2. If not yet authorized, generate a magic link for you to complete OAuth 3. Fetch the `notion_page_get` tool schema 4. Return the page content ## Supported connectors [Section titled “Supported connectors”](#supported-connectors) Any connector configured in Scalekit works with the OpenClaw skill, including Notion, Slack, Gmail, Google Sheets, GitHub, Salesforce, HubSpot, Linear, Snowflake, Exa, HarvestAPI, and 50+ more. [Browse connections ](/agentkit/connectors/)See all supported connectors in the Scalekit dashboard [ClawHub listing ](https://clawhub.dev/skills/scalekit-agent-auth)Install scalekit-agent-auth from ClawHub ## Common scenarios [Section titled “Common scenarios”](#common-scenarios)

---
# DOCUMENT BOUNDARY
---

# Node.js SDK reference

> Complete API reference for the Scalekit Node.js SDK: actions client and tools client.

`scalekit.actions` is the primary interface for AgentKit. It handles connected account management, tool execution, and proxied API calls. `scalekit.tools` exposes raw tool schemas for building custom adapters. ## Install and initialize [Section titled “Install and initialize”](#install-and-initialize) ```bash 1 npm install @scalekit-sdk/node ``` ```ts 1 import { ScalekitClient } from '@scalekit-sdk/node'; 2 3 const scalekit = new ScalekitClient({ 4 clientId: process.env.SCALEKIT_CLIENT_ID!, 5 clientSecret: process.env.SCALEKIT_CLIENT_SECRET!, 6 envUrl: process.env.SCALEKIT_ENV_URL!, 7 }); ``` *** ## Actions client [Section titled “Actions client”](#actions-client) ### Authentication [Section titled “Authentication”](#authentication) #### getAuthorizationLink [Section titled “getAuthorizationLink”](#getauthorizationlink) Generates a time-limited OAuth magic link to authorize a user’s connection. Input schema NameTypeRequiredDescription connectionNamestringoptionalConnector slug (e.g. gmail) identifierstringoptionalUser's identifier (e.g. email) connectedAccountIdstringoptionalDirect connected account ID (ca\_...) organizationIdstringoptionalOrganization tenant ID when your app scopes auth and accounts by org userIdstringoptionalYour application user ID when you map Scalekit accounts to internal users statestringoptionalOpaque value passed through to the redirect URL userVerifyUrlstringoptionalYour app's redirect URL for user verification Response schema GetMagicLinkForConnectedAccountResponse Field Type Description link string OAuth magic link URL. Redirect the user here to start the authorization flow. Example ```ts 1 const { link } = await scalekit.actions.getAuthorizationLink({ 2 connectionName: 'gmail', 3 identifier: 'user@example.com', 4 userVerifyUrl: 'https://your-app.com/verify', 5 }); 6 // Redirect the user to link ``` #### verifyConnectedAccountUser [Section titled “verifyConnectedAccountUser”](#verifyconnectedaccountuser) Verifies the user after OAuth callback. Call this from your redirect URL handler. Input schema NameTypeRequiredDescription authRequestIdstringrequiredToken from the redirect URL query params identifierstringrequiredCurrent user's identifier Response schema VerifyConnectedAccountUserResponse Field Type Description postUserVerifyRedirectUrl string URL to redirect the user to after successful verification Example ```ts 1 await scalekit.actions.verifyConnectedAccountUser({ 2 authRequestId: req.query.auth_request_id as string, 3 identifier: 'user@example.com', 4 }); ``` *** ### Connected accounts [Section titled “Connected accounts”](#connected-accounts) #### getOrCreateConnectedAccount [Section titled “getOrCreateConnectedAccount”](#getorcreateconnectedaccount) Fetches an existing connected account or creates one if none exists. Use this as the default when setting up a user. Input schema NameTypeRequiredDescription connectionNamestringrequiredConnector slug identifierstringrequiredUser's identifier authorizationDetailsobjectoptionalOAuth token or static auth details organizationIdstringoptionalOrganization tenant ID when your app scopes auth and accounts by org userIdstringoptionalYour application user ID when you map Scalekit accounts to internal users apiConfigRecord\optionalConnector-specific options (for example scopes or static auth fields) Response schema CreateConnectedAccountResponse Field Type Description connectedAccount.id string Account ID (ca\_...) connectedAccount.identifier string User's identifier connectedAccount.provider string Provider slug connectedAccount.status string ACTIVE, INACTIVE, or PENDING connectedAccount.authorizationType string OAuth, API\_KEY, etc. connectedAccount.tokenExpiresAt string ISO 8601 OAuth token expiry Example ```ts 1 const { connectedAccount } = await scalekit.actions.getOrCreateConnectedAccount({ 2 connectionName: 'gmail', 3 identifier: 'user@example.com', 4 }); 5 console.log(connectedAccount.id); ``` #### getConnectedAccount [Section titled “getConnectedAccount”](#getconnectedaccount) Fetches auth details for a connected account. Returns sensitive credentials. Protect access to this method. Requires `connectedAccountId` **or** `connectionName` + `identifier`. Input schema NameTypeRequiredDescription connectionNamestringoptionalConnector slug. Use with identifier when you do not pass connectedAccountId. identifierstringoptionalEnd-user or workspace identifier. Use with connectionName. connectedAccountIdstringoptionalConnected account ID (ca\_...) when resolving by ID instead of name + identifier organizationIdstringoptionalOrganization tenant ID when your app scopes auth and accounts by org userIdstringoptionalYour application user ID when you map Scalekit accounts to internal users Response schema GetConnectedAccountByIdentifierResponse Field Type Description connectedAccount.id string Account ID (ca\_...) connectedAccount.identifier string User's identifier connectedAccount.provider string Provider slug connectedAccount.status string ACTIVE, INACTIVE, or PENDING connectedAccount.authorizationType string OAuth, API\_KEY, etc. connectedAccount.authorizationDetails object Credential payload (access token, API key, etc.) connectedAccount.tokenExpiresAt string ISO 8601 OAuth token expiry connectedAccount.lastUsedAt string Last time this account was used connectedAccount.updatedAt string Last update timestamp #### listConnectedAccounts [Section titled “listConnectedAccounts”](#listconnectedaccounts) Input schema NameTypeRequiredDescription connectionNamestringoptionalFilter by connector identifierstringoptionalFilter by user identifier providerstringoptionalFilter by provider organizationIdstringoptionalOrganization tenant ID when your app scopes auth and accounts by org userIdstringoptionalYour application user ID when you map Scalekit accounts to internal users pageSizenumberoptionalMaximum accounts per page (server default if omitted) pageTokenstringoptionalOpaque cursor from a previous list response querystringoptionalFree-text search Response schema ListConnectedAccountsResponse Field Type Description connectedAccounts array List of ConnectedAccountForList objects (excludes authorizationDetails) totalSize number Total number of matching accounts nextPageToken string Token for the next page, if any prevPageToken string Token for the previous page, if any #### createConnectedAccount [Section titled “createConnectedAccount”](#createconnectedaccount) Creates a connected account with explicit auth details. Input schema NameTypeRequiredDescription connectionNamestringrequiredConnector slug. Must match a connection configured in your environment. identifierstringrequiredStable ID for this end user or workspace (email, user\_id, or custom string) authorizationDetailsobjectrequiredOAuth token payload, API key, or other credentials for this connector organizationIdstringoptionalOrganization tenant ID when your app scopes auth and accounts by org userIdstringoptionalYour application user ID when you map Scalekit accounts to internal users apiConfigRecord\optionalConnector-specific options (for example scopes or static auth fields) Returns CreateConnectedAccountResponse. Same shape as `getOrCreateConnectedAccount`. #### updateConnectedAccount [Section titled “updateConnectedAccount”](#updateconnectedaccount) Requires `connectedAccountId` **or** `connectionName` + `identifier`. Input schema NameTypeRequiredDescription connectionNamestringoptionalConnector slug. Use with identifier when you do not pass connectedAccountId. identifierstringoptionalEnd-user or workspace identifier. Use with connectionName. connectedAccountIdstringoptionalConnected account ID (ca\_...) when updating by ID instead of name + identifier authorizationDetailsobjectoptionalReplace or merge stored credentials (OAuth tokens, API keys, etc.) organizationIdstringoptionalOrganization tenant ID when your app scopes auth and accounts by org userIdstringoptionalYour application user ID when you map Scalekit accounts to internal users apiConfigobjectoptionalConnector-specific configuration to persist on the account Returns UpdateConnectedAccountResponse. #### deleteConnectedAccount [Section titled “deleteConnectedAccount”](#deleteconnectedaccount) Deletes a connected account and revokes its credentials. Requires `connectedAccountId` **or** `connectionName` + `identifier`. Input schema NameTypeRequiredDescription connectionNamestringoptionalConnector slug. Use with identifier when you do not pass connectedAccountId. identifierstringoptionalEnd-user or workspace identifier. Use with connectionName. connectedAccountIdstringoptionalConnected account ID (ca\_...) when deleting by ID instead of name + identifier organizationIdstringoptionalOrganization tenant ID when your app scopes auth and accounts by org userIdstringoptionalYour application user ID when you map Scalekit accounts to internal users Returns DeleteConnectedAccountResponse. *** ### Tool execution [Section titled “Tool execution”](#tool-execution) #### executeTool [Section titled “executeTool”](#executetool) Executes a named tool via Scalekit. Input schema NameTypeRequiredDescription toolNamestringrequiredTool name (e.g. gmail\_fetch\_emails) toolInputRecord\requiredParameters the tool expects identifierstringoptionalUser's identifier connectedAccountIdstringoptionalDirect connected account ID connectorstringoptionalConnector slug organizationIdstringoptionalOrganization tenant ID when your app scopes auth and accounts by org userIdstringoptionalYour application user ID when you map Scalekit accounts to internal users Response schema ExecuteToolResponse Field Type Description data object Tool's structured output executionId string Unique ID for this execution Example ```ts 1 const result = await scalekit.actions.executeTool({ 2 toolName: 'gmail_fetch_emails', 3 toolInput: { maxResults: 5, label: 'UNREAD' }, 4 identifier: 'user@example.com', 5 }); 6 const emails = result.data; ``` *** ### Proxied API calls [Section titled “Proxied API calls”](#proxied-api-calls) #### request [Section titled “request”](#request) Makes a REST API call on behalf of a connected account. Scalekit injects the user’s OAuth token automatically. Input schema NameTypeRequiredDescription connectionNamestringrequiredConnector slug identifierstringrequiredUser's identifier pathstringrequiredAPI path (e.g. /gmail/v1/users/me/messages) methodstringoptionalHTTP method. Default: GET queryParamsRecord\optionalURL query parameters appended to path bodyunknownoptionalJSON-serializable body for POST, PUT, PATCH, or similar methods formDataRecord\optionalMultipart form fields when the upstream API expects form data instead of JSON headersRecord\optionalExtra HTTP headers merged with Scalekit-injected auth headers timeoutMsnumberoptionalDefault: 30000 Returns `AxiosResponse`. Use `.data`, `.status`, and standard Axios response attributes. Example ```ts 1 const response = await scalekit.actions.request({ 2 connectionName: 'gmail', 3 identifier: 'user@example.com', 4 path: '/gmail/v1/users/me/messages', 5 queryParams: { maxResults: 5, q: 'is:unread' }, 6 }); 7 const messages = response.data.messages; ``` *** ## Tools client [Section titled “Tools client”](#tools-client) `scalekit.tools` gives access to raw tool schemas. Use this when building a custom framework adapter or passing schemas directly to an LLM API (e.g. Anthropic, OpenAI). #### listTools [Section titled “listTools”](#listtools) Lists all tools available in your workspace. Input schema NameTypeRequiredDescription filterFilteroptionalFilter by provider, identifier, or tool name pageSizenumberoptionalMaximum tools per page (server default if omitted) pageTokenstringoptionalOpaque cursor from a previous list response Response schema ListToolsResponse Field Type Description tools array List of tool schemas (name, description, input schema) nextPageToken string Token for the next page, if any #### listScopedTools [Section titled “listScopedTools”](#listscopedtools) Lists tools scoped to a specific user. Use this for tool discovery because it returns pagination metadata such as `nextPageToken` and `totalSize`. Input schema NameTypeRequiredDescription identifierstringrequiredUser's connected account identifier filterScopedToolFilteroptionalFilter by providers, tool names, or connection names pageSizenumberoptionalMaximum tools per page. Use 100 for discovery so connectors with more than the default page are not truncated. pageTokenstringoptionalOpaque cursor from a previous list response Response schema ListScopedToolsResponse Field Type Description tools array List of tool schemas tools\[].name string Tool name tools\[].description string Tool description tools\[].inputSchema object JSON Schema for tool inputs. Pass directly to LLM API. nextPageToken string Token for the next page, if any Example ```ts 1 const { tools } = await scalekit.tools.listScopedTools('user@example.com', { 2 filter: { connectionNames: ['gmail'] }, 3 pageSize: 100, 4 }); 5 // Pass tools to your LLM's tool call API ``` #### listAvailableTools [Section titled “listAvailableTools”](#listavailabletools) Lists tools available for a given identifier. These tools can be activated but may not yet be scoped to the user. Input schema NameTypeRequiredDescription identifierstringrequiredUser's connected account identifier pageSizenumberoptionalMaximum tools per page (server default if omitted) pageTokenstringoptionalOpaque cursor from a previous list response Response schema ListAvailableToolsResponse Field Type Description tools array List of available tool schemas nextPageToken string Token for the next page, if any #### executeTool [Section titled “executeTool”](#executetool-1) Low-level tool execution. Prefer `scalekit.actions.executeTool` for most use cases. Input schema NameTypeRequiredDescription toolNamestringrequiredRegistered tool name to execute identifierstringoptionalEnd-user or workspace identifier used to resolve the connected account paramsRecord\optionalTool arguments matching the tool input schema connectedAccountIdstringoptionalConnected account ID (ca\_...) when you already know it connectorstringoptionalConnector slug when the tool name exists on more than one connector organizationIdstringoptionalOrganization tenant ID when your app scopes auth and accounts by org userIdstringoptionalYour application user ID when you map Scalekit accounts to internal users Returns ExecuteToolResponse. Same shape as `scalekit.actions.executeTool`. *** ## Error handling [Section titled “Error handling”](#error-handling) ```ts 1 import { 2 ScalekitNotFoundException, 3 ScalekitServerException, 4 } from '@scalekit-sdk/node'; 5 6 try { 7 const account = await scalekit.actions.getConnectedAccount({ 8 connectionName: 'gmail', 9 identifier: 'user@example.com', 10 }); 11 } catch (err) { 12 if (err instanceof ScalekitNotFoundException) { 13 // Account does not exist: create it or redirect to auth 14 } else if (err instanceof ScalekitServerException) { 15 // Network or server error 16 console.error(err); 17 } 18 } ``` | Exception | When raised | | ------------------------------- | -------------------------------- | | `ScalekitNotFoundException` | Resource not found | | `ScalekitUnauthorizedException` | Invalid credentials | | `ScalekitForbiddenException` | Insufficient permissions | | `ScalekitServerException` | Base class for all server errors |

---
# DOCUMENT BOUNDARY
---

# Python SDK reference

> Complete API reference for the Scalekit Python SDK: actions client, MCP server provisioning, framework adapters, tools client, and modifiers.

`scalekit_client.actions` is the primary interface for AgentKit. It handles connected account management, MCP server provisioning, tool execution, and framework integrations. ## Install and initialize [Section titled “Install and initialize”](#install-and-initialize) ```bash 1 pip install scalekit-sdk-python ``` ```python 1 import os 2 import scalekit.client 3 4 scalekit_client = scalekit.client.ScalekitClient( 5 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 6 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 ) 9 10 actions = scalekit_client.actions ``` *** ## Actions client [Section titled “Actions client”](#actions-client) ### Authentication [Section titled “Authentication”](#authentication) #### get\_authorization\_link [Section titled “get\_authorization\_link”](#get_authorization_link) Generates a time-limited OAuth magic link to authorize a user’s connection. Input schema NameTypeRequiredDescription identifierstroptionalUser identifier (e.g. email) connection\_namestroptionalConnector slug (e.g. gmail) connected\_account\_idstroptionalDirect connected account ID (ca\_...) statestroptionalOpaque value passed through to the redirect URL user\_verify\_urlstroptionalApp redirect URL for user verification Response schema MagicLinkResponse Field Type Description link str OAuth magic link URL. Redirect the user here to start the authorization flow. expiry datetime Link expiry timestamp Example ```python 1 magic_link = actions.get_authorization_link( 2 identifier="user@example.com", 3 connection_name="gmail", 4 user_verify_url="https://your-app.com/verify", 5 ) 6 # Redirect the user to magic_link.link ``` #### verify\_connected\_account\_user [Section titled “verify\_connected\_account\_user”](#verify_connected_account_user) Verifies the user after OAuth callback. Call this from your redirect URL handler. Input schema NameTypeRequiredDescription auth\_request\_idstrrequiredToken from the redirect URL query params identifierstrrequiredCurrent user identifier Response schema VerifyConnectedAccountUserResponse Field Type Description post\_user\_verify\_redirect\_url str URL to redirect the user to after successful verification Example ```python 1 result = actions.verify_connected_account_user( 2 auth_request_id=request.args["auth_request_id"], 3 identifier="user@example.com", 4 ) 5 # Redirect to result.post_user_verify_redirect_url ``` *** ### Connected accounts [Section titled “Connected accounts”](#connected-accounts) #### get\_or\_create\_connected\_account [Section titled “get\_or\_create\_connected\_account”](#get_or_create_connected_account) Fetches an existing connected account or creates one if none exists. Use this as the default when setting up a user. Input schema NameTypeRequiredDescription connection\_namestrrequiredConnector slug identifierstrrequiredUser's identifier authorization\_detailsdictoptionalOAuth token or static auth details organization\_idstroptionalOrganization tenant ID when your app scopes auth and accounts by org user\_idstroptionalYour application user ID when you map Scalekit accounts to internal users api\_configdictoptionalConnector-specific options (for example scopes or static auth fields) Response schema CreateConnectedAccountResponse Field Type Description connected\_account.id str Account ID (ca\_...) connected\_account.identifier str User's identifier connected\_account.provider str Provider slug connected\_account.status str ACTIVE, INACTIVE, or PENDING connected\_account.authorization\_type str OAuth, API\_KEY, etc. connected\_account.token\_expires\_at datetime OAuth token expiry Example ```python 1 account = actions.get_or_create_connected_account( 2 connection_name="gmail", 3 identifier="user@example.com", 4 ) 5 print(account.connected_account.id) ``` #### get\_connected\_account [Section titled “get\_connected\_account”](#get_connected_account) Fetches auth details for a connected account. Returns sensitive credentials. Protect access to this method. Use this when you know the connected account already exists and you need its credential payload. For first-time setup or general application flows, prefer `get_or_create_connected_account` so new users do not hit a not-found error. Requires `connected_account_id` **or** `connection_name` + `identifier`. Input schema NameTypeRequiredDescription connection\_namestroptionalConnector slug. Use with identifier when you do not pass connected\_account\_id. identifierstroptionalEnd-user or workspace identifier. Use with connection\_name. connected\_account\_idstroptionalConnected account ID (ca\_...) when resolving by ID instead of name + identifier Response schema GetConnectedAccountAuthResponse Field Type Description connected\_account.id str Account ID (ca\_...) connected\_account.identifier str User's identifier connected\_account.provider str Provider slug connected\_account.status str ACTIVE, INACTIVE, or PENDING connected\_account.authorization\_type str OAuth, API\_KEY, etc. connected\_account.authorization\_details dict Credential payload (access token, API key, etc.) connected\_account.token\_expires\_at datetime OAuth token expiry connected\_account.last\_used\_at datetime Last time this account was used connected\_account.updated\_at datetime Last update timestamp #### list\_connected\_accounts [Section titled “list\_connected\_accounts”](#list_connected_accounts) Input schema NameTypeRequiredDescription connection\_namestroptionalFilter by connector identifierstroptionalFilter by user identifier providerstroptionalFilter by provider Response schema ListConnectedAccountsResponse Field Type Description connected\_accounts list List of ConnectedAccountForList objects (excludes authorization\_details and api\_config) total\_count int Total number of matching accounts next\_page\_token str Token for the next page, if any previous\_page\_token str Token for the previous page, if any #### create\_connected\_account [Section titled “create\_connected\_account”](#create_connected_account) Creates a connected account with explicit auth details. Input schema NameTypeRequiredDescription connection\_namestrrequiredConnector slug. Must match a connection configured in your environment. identifierstrrequiredStable ID for this end user or workspace (email, user\_id, or custom string) authorization\_detailsdictrequiredOAuth token payload, API key, or other credentials for this connector organization\_idstroptionalOrganization tenant ID when your app scopes auth and accounts by org user\_idstroptionalYour application user ID when you map Scalekit accounts to internal users api\_configdictoptionalConnector-specific options (for example scopes or static auth fields) Returns CreateConnectedAccountResponse. Same shape as `get_or_create_connected_account`. #### update\_connected\_account [Section titled “update\_connected\_account”](#update_connected_account) Requires `connected_account_id` **or** `connection_name` + `identifier`. Input schema NameTypeRequiredDescription connection\_namestroptionalConnector slug. Use with identifier when you do not pass connected\_account\_id. identifierstroptionalEnd-user or workspace identifier. Use with connection\_name. connected\_account\_idstroptionalConnected account ID (ca\_...) when updating by ID instead of name + identifier authorization\_detailsdictoptionalReplace or merge stored credentials (OAuth tokens, API keys, etc.) organization\_idstroptionalOrganization tenant ID when your app scopes auth and accounts by org user\_idstroptionalYour application user ID when you map Scalekit accounts to internal users api\_configdictoptionalConnector-specific configuration to persist on the account Returns UpdateConnectedAccountResponse. #### delete\_connected\_account [Section titled “delete\_connected\_account”](#delete_connected_account) Deletes a connected account and revokes its credentials. Requires `connected_account_id` **or** `connection_name` + `identifier`. Input schema NameTypeRequiredDescription connection\_namestroptionalConnector slug. Use with identifier when you do not pass connected\_account\_id. identifierstroptionalEnd-user or workspace identifier. Use with connection\_name. connected\_account\_idstroptionalConnected account ID (ca\_...) when deleting by ID instead of name + identifier Returns DeleteConnectedAccountResponse. *** ### Tool execution [Section titled “Tool execution”](#tool-execution) #### execute\_tool [Section titled “execute\_tool”](#execute_tool) Executes a named tool via Scalekit. Pre- and post-modifiers run automatically if registered. Input schema NameTypeRequiredDescription tool\_namestrrequiredTool name (e.g. gmail\_fetch\_emails) tool\_inputdictrequiredParameters the tool expects identifierstroptionalUser's identifier connected\_account\_idstroptionalDirect connected account ID Response schema ExecuteToolResponse Field Type Description data dict Tool structured output execution\_id str Unique ID for this execution Example ```python 1 result = actions.execute_tool( 2 tool_name="gmail_fetch_emails", 3 tool_input={"max_results": 5, "label": "UNREAD"}, 4 identifier="user@example.com", 5 ) 6 emails = result.data ``` *** ### Proxied API calls [Section titled “Proxied API calls”](#proxied-api-calls) #### request [Section titled “request”](#request) Makes a REST API call on behalf of a connected account. Scalekit injects the user’s OAuth token automatically. Input schema NameTypeRequiredDescription connection\_namestrrequiredConnector slug identifierstrrequiredUser's identifier pathstrrequiredAPI path (e.g. /gmail/v1/users/me/messages) methodstroptionalHTTP method. Default: GET query\_paramsdictoptionalURL query parameters appended to path bodyanyoptionalJSON-serializable body for POST, PUT, PATCH, or similar methods form\_datadictoptionalMultipart form fields when the upstream API expects form data instead of JSON headersdictoptionalExtra HTTP headers merged with Scalekit-injected auth headers Returns `requests.Response`. Use `.json()`, `.status_code`, and standard response attributes. Example ```python 1 response = actions.request( 2 connection_name="gmail", 3 identifier="user@example.com", 4 path="/gmail/v1/users/me/messages", 5 query_params={"maxResults": 5, "q": "is:unread"}, 6 ) 7 messages = response.json()["messages"] ``` *** ## MCP server provisioning [Section titled “MCP server provisioning”](#mcp-server-provisioning) `actions.mcp` generates per-user MCP-compatible server URLs. Any MCP-compatible agent framework (LangChain, Google ADK, Anthropic, OpenAI, and others) can connect to these URLs directly. **Two-step model:** Create a **config** once (defines which connectors and tools to expose), then call `ensure_instance` per user to get their personal MCP server URL. ### Configs [Section titled “Configs”](#configs) #### actions.mcp.create\_config [Section titled “actions.mcp.create\_config”](#actionsmcpcreate_config) Input schema NameTypeRequiredDescription namestrrequiredConfig name descriptionstroptionalHuman-readable summary of what this MCP config exposes connection\_tool\_mappingslistoptionalList of McpConfigConnectionToolMapping objects Response schema CreateMcpConfigResponse Field Type Description config.id str Config ID config.name str Config name config.connection\_tool\_mappings list Connector-to-tools mappings Example ```python 1 from scalekit.actions.types import McpConfigConnectionToolMapping 2 3 config = actions.mcp.create_config( 4 name="email-agent", 5 connection_tool_mappings=[ 6 McpConfigConnectionToolMapping( 7 connection_name="gmail", 8 tools=["gmail_fetch_emails", "gmail_send_email"], 9 ) 10 ], 11 ) ``` #### actions.mcp.list\_configs [Section titled “actions.mcp.list\_configs”](#actionsmcplist_configs) Input schema NameTypeRequiredDescription page\_sizeintoptionalMaximum configs per page (server default if omitted) page\_tokenstroptionalOpaque cursor from a previous list response filter\_namestroptionalFilter by exact name filter\_providerstroptionalFilter by provider slug searchstroptionalFree-text search on name Returns ListMcpConfigsResponse. #### actions.mcp.update\_config [Section titled “actions.mcp.update\_config”](#actionsmcpupdate_config) Input schema NameTypeRequiredDescription config\_idstrrequiredMCP config ID from create\_config or list\_configs descriptionstroptionalNew human-readable description for this config connection\_tool\_mappingslistoptionalReplaces existing mappings Returns UpdateMcpConfigResponse. #### actions.mcp.delete\_config [Section titled “actions.mcp.delete\_config”](#actionsmcpdelete_config) Input schema NameTypeRequiredDescription config\_idstrrequiredMCP config ID to delete Returns DeleteMcpConfigResponse. ### Instances [Section titled “Instances”](#instances) #### actions.mcp.ensure\_instance [Section titled “actions.mcp.ensure\_instance”](#actionsmcpensure_instance) Creates an MCP instance for this user if one doesn’t exist, or returns the existing one. Call this on every session; it’s idempotent. The `instance.url` field is the MCP server URL to give to the user’s agent or IDE. Input schema NameTypeRequiredDescription config\_namestrrequiredName of the config to instantiate user\_identifierstrrequiredUser identifier (e.g. email) namestroptionalDisplay name for the instance Response schema EnsureMcpInstanceResponse Field Type Description instance.url str MCP server URL for agent or IDE instance.id str Instance ID instance.name str Display name instance.user\_identifier str User identifier instance.config object The config this instance was created from instance.last\_used\_at datetime Last usage timestamp instance.updated\_at datetime Last update timestamp Example ```python 1 instance = actions.mcp.ensure_instance( 2 config_name="email-agent", 3 user_identifier="user@example.com", 4 ) 5 mcp_url = instance.instance.url 6 # Give mcp_url to the user's agent or IDE ``` #### actions.mcp.get\_instance\_auth\_state [Section titled “actions.mcp.get\_instance\_auth\_state”](#actionsmcpget_instance_auth_state) Returns authorization status per connector. Use `include_auth_links=True` to generate fresh auth links for connections that need authorization or re-authorization. Input schema NameTypeRequiredDescription instance\_idstrrequiredInstance ID include\_auth\_linksbooloptionalGenerate auth links for unauthorized connections Response schema GetMcpInstanceAuthStateResponse Field Type Description connections list List of McpInstanceConnectionAuthState, one per configured connector connections\[].connection\_id str Connection ID connections\[].connection\_name str Connector slug connections\[].provider str Provider slug connections\[].connected\_account\_id str Connected account ID, if authorized connections\[].connected\_account\_status str ACTIVE, INACTIVE, or PENDING connections\[].authentication\_link str Auth link to send to the user when status is not ACTIVE Example ```python 1 auth_state = actions.mcp.get_instance_auth_state( 2 instance_id=instance.instance.id, 3 include_auth_links=True, 4 ) 5 for conn in auth_state.connections: 6 if conn.connected_account_status != "ACTIVE": 7 # Send conn.authentication_link to the user to authorize 8 print(f"{conn.connection_name}: {conn.authentication_link}") ``` #### actions.mcp.get\_instance [Section titled “actions.mcp.get\_instance”](#actionsmcpget_instance) Input schema NameTypeRequiredDescription instance\_idstrrequiredMCP instance ID from ensure\_instance or list\_instances Returns GetMcpInstanceResponse. #### actions.mcp.list\_instances [Section titled “actions.mcp.list\_instances”](#actionsmcplist_instances) Input schema NameTypeRequiredDescription page\_sizeintoptionalMaximum instances per page (server default if omitted) page\_tokenstroptionalOpaque cursor from a previous list response filter\_user\_identifierstroptionalFilter by user filter\_config\_namestroptionalFilter by config name filter\_namestroptionalFilter by MCP instance display name filter\_idstroptionalFilter by MCP instance ID Returns ListMcpInstancesResponse. #### actions.mcp.update\_instance [Section titled “actions.mcp.update\_instance”](#actionsmcpupdate_instance) At least one of `name` or `config_name` is required. Input schema NameTypeRequiredDescription instance\_idstrrequiredMCP instance ID to update namestroptionalNew display name for this instance config\_namestroptionalSwitch the instance to a different config by name Returns UpdateMcpInstanceResponse. #### actions.mcp.delete\_instance [Section titled “actions.mcp.delete\_instance”](#actionsmcpdelete_instance) Input schema NameTypeRequiredDescription instance\_idstrrequiredMCP instance ID to delete Returns DeleteMcpInstanceResponse. *** ## Framework adapters [Section titled “Framework adapters”](#framework-adapters) Pre-built integrations for LangChain and Google ADK. Use these when your agent runs in one of these frameworks and you prefer native tool objects over an MCP URL. ### LangChain [Section titled “LangChain”](#langchain) ```bash 1 pip install langchain ``` #### actions.langchain.get\_tools [Section titled “actions.langchain.get\_tools”](#actionslangchainget_tools) Input schema NameTypeRequiredDescription identifierstrrequiredUser connected account identifier providerslistoptionalFilter by provider (e.g. \["google"]) tool\_nameslistoptionalFilter by tool name connection\_nameslistoptionalFilter by connection name page\_sizeintoptionalMaximum tools per page. Use 100 for discovery so connectors with more than the default page are not truncated. page\_tokenstroptionalOpaque cursor from a previous list response Response schema List\[StructuredTool] Field Type Description \[].name str Tool name \[].description str Tool description \[].args\_schema object Pydantic schema for the tool inputs Example ```python 1 from langchain.agents import create_react_agent 2 3 tools = actions.langchain.get_tools( 4 identifier="user@example.com", 5 page_size=100, # avoid missing tools when a connector has more than the default page 6 ) 7 agent = create_react_agent(llm=llm, tools=tools, prompt=prompt) ``` ### Google ADK [Section titled “Google ADK”](#google-adk) ```bash 1 pip install google-adk ``` #### actions.google.get\_tools [Section titled “actions.google.get\_tools”](#actionsgoogleget_tools) Same parameters as `actions.langchain.get_tools`. Returns `List[ScalekitGoogleAdkTool]`. Pass it directly to a Google ADK agent. Example ```python 1 tools = actions.google.get_tools( 2 identifier="user@example.com", 3 page_size=100, # avoid missing tools when a connector has more than the default page 4 ) ``` *** ## Tools client [Section titled “Tools client”](#tools-client) `scalekit_client.actions.tools` gives access to raw tool schemas. Use this when building a custom adapter or passing schemas directly to an LLM API (e.g. Anthropic, OpenAI). #### actions.tools.list\_tools [Section titled “actions.tools.list\_tools”](#actionstoolslist_tools) Input schema NameTypeRequiredDescription filterFilteroptionalFilter by provider, identifier, or tool name page\_sizeintoptionalMaximum tools per page. Use 100 for discovery so connectors with more than the default page are not truncated. page\_tokenstroptionalOpaque cursor from a previous list response Response schema ListToolsResponse Field Type Description tools list List of tool schemas (name, description, input schema) next\_page\_token str Token for the next page, if any #### actions.tools.list\_scoped\_tools [Section titled “actions.tools.list\_scoped\_tools”](#actionstoolslist_scoped_tools) Lists tools scoped to a specific user. Use this method for tool discovery because it returns pagination metadata such as `next_page_token` and `total_size`; framework `get_tools()` helpers return framework-ready tool objects and do not expose that metadata. Input schema NameTypeRequiredDescription identifierstrrequiredUser connected account identifier filterScopedToolFilteroptionalFilter by providers, tool names, or connection names page\_sizeintoptionalMaximum tools per page. Use 100 for discovery so connectors with more than the default page are not truncated. page\_tokenstroptionalOpaque cursor from a previous list response Response schema ListScopedToolsResponse Field Type Description tools list List of tool schemas (name, description, input\_schema) tools\[].name str Tool name tools\[].description str Tool description tools\[].input\_schema object JSON Schema for tool inputs. Pass directly to LLM API. next\_page\_token str Token for the next page, if any Example ```python 1 tools_response = scalekit_client.actions.tools.list_scoped_tools( 2 identifier="user@example.com", 3 page_size=100, 4 ) 5 # Pass tools_response.tools to your LLM's tool call API ``` #### actions.tools.execute\_tool [Section titled “actions.tools.execute\_tool”](#actionstoolsexecute_tool) Low-level tool execution. Bypasses modifiers. Prefer `actions.execute_tool` in most cases. Input schema NameTypeRequiredDescription tool\_namestrrequiredRegistered tool name to execute identifierstrrequiredEnd-user or workspace identifier used to resolve the connected account paramsdictoptionalTool arguments matching the tool input schema connected\_account\_idstroptionalConnected account ID (ca\_...) when you already know it Returns ExecuteToolResponse. Same shape as `actions.execute_tool`. *** ## Modifiers [Section titled “Modifiers”](#modifiers) Modifiers intercept tool calls to transform inputs or outputs, useful for validation, enrichment, or logging. ```python 1 @actions.pre_modifier(tool_names=["gmail_fetch_emails"]) 2 def add_default_label(tool_input): 3 tool_input.setdefault("label", "UNREAD") 4 return tool_input 5 6 @actions.post_modifier(tool_names=["gmail_fetch_emails"]) 7 def filter_attachments(tool_output): 8 tool_output["emails"] = [e for e in tool_output["emails"] if not e.get("has_attachment")] 9 return tool_output ``` | Decorator | Receives | Returns | | ------------------------------------ | -------- | --------------- | | `@actions.pre_modifier(tool_names)` | `dict` | Modified `dict` | | `@actions.post_modifier(tool_names)` | `dict` | Modified `dict` | `tool_names` accepts a string or a list of strings. Multiple modifiers for the same tool chain in registration order. *** ## Error handling [Section titled “Error handling”](#error-handling) ```python 1 from scalekit.common.exceptions import ScalekitNotFoundException, ScalekitServerException 2 3 try: 4 account = actions.get_connected_account( 5 connection_name="gmail", 6 identifier="user@example.com", 7 ) 8 except ScalekitNotFoundException: 9 # Account does not exist: create it or redirect to auth 10 pass 11 except ScalekitServerException as e: 12 print(e.error_code, e.http_status) ``` | Exception | When raised | | ------------------------------- | -------------------------------- | | `ScalekitNotFoundException` | Resource not found | | `ScalekitUnauthorizedException` | Invalid credentials | | `ScalekitForbiddenException` | Insufficient permissions | | `ScalekitServerException` | Base class for all server errors |

---
# DOCUMENT BOUNDARY
---

# Authorize a user

> Generate an authorization link, send it to your user, and confirm their connected account is active before your agent executes tools.

Once a connection is configured, your users need to grant your agent access to their account. This happens once per user per connection. Scalekit stores their tokens and keeps them fresh automatically. The flow is: 1. Create a connected account for the user 2. Generate an authorization link and send it to the user 3. The user completes the OAuth consent screen 4. The connected account becomes `ACTIVE`. Your agent can now execute tools. ## Create a connected account and generate a link [Section titled “Create a connected account and generate a link”](#create-a-connected-account-and-generate-a-link) * Python ```python 1 # Create or retrieve the connected account for this user 2 response = actions.get_or_create_connected_account( 3 connection_name="gmail", 4 identifier="user_123" # your app's unique user ID 5 ) 6 connected_account = response.connected_account 7 8 # Generate the authorization link if the account is not yet active 9 if connected_account.status != "ACTIVE": 10 link_response = actions.get_authorization_link( 11 connection_name="gmail", 12 identifier="user_123" 13 ) 14 auth_url = link_response.link 15 # Redirect or send auth_url to the user ``` * Node.js ```typescript 1 import { ConnectorStatus } from '@scalekit-sdk/node/lib/pkg/grpc/scalekit/v1/connected_accounts/connected_accounts_pb'; 2 3 // Create or retrieve the connected account for this user 4 const response = await actions.getOrCreateConnectedAccount({ 5 connectionName: 'gmail', 6 identifier: 'user_123', // your app's unique user ID 7 }); 8 9 const connectedAccount = response.connectedAccount; 10 11 // Generate the authorization link if the account is not yet active 12 if (connectedAccount?.status !== ConnectorStatus.ACTIVE) { 13 const linkResponse = await actions.getAuthorizationLink({ 14 connectionName: 'gmail', 15 identifier: 'user_123', 16 }); 17 const authUrl = linkResponse.link; 18 // Redirect or send authUrl to the user 19 } ``` ## Send the link to the user [Section titled “Send the link to the user”](#send-the-link-to-the-user) How you deliver the link depends on your application: * **Web app:** redirect the user to `auth_url` directly if they’re in an active browser session * **Email or notification:** send the link when the user isn’t actively in your app, or when connecting at their own pace is acceptable * **In-app prompt:** show a button (“Connect Gmail”) when you want to prompt connection at a specific moment in the user’s workflow Once the user opens the link and approves the OAuth consent screen, Scalekit exchanges the authorization code for tokens and marks the connected account `ACTIVE`. You do not need to handle the OAuth callback yourself. ## Check status and re-authorize [Section titled “Check status and re-authorize”](#check-status-and-re-authorize) Check the connected account status before executing tools. Tokens can expire or be revoked, so generate a new authorization link using the same flow when that happens. * Python ```python 1 response = actions.get_or_create_connected_account( 2 connection_name="gmail", 3 identifier="user_123" 4 ) 5 connected_account = response.connected_account 6 # ACTIVE: ready for tool calls 7 # PENDING: user has not completed the OAuth flow 8 # EXPIRED: tokens expired, re-authorization required 9 # REVOKED: user revoked access from the provider 10 11 if connected_account.status != "ACTIVE": 12 link_response = actions.get_authorization_link( 13 connection_name="gmail", 14 identifier="user_123" 15 ) 16 # Redirect or send link_response.link to the user ``` * Node.js ```typescript 1 import { ConnectorStatus } from '@scalekit-sdk/node/lib/pkg/grpc/scalekit/v1/connected_accounts/connected_accounts_pb'; 2 3 const response = await actions.getOrCreateConnectedAccount({ 4 connectionName: 'gmail', 5 identifier: 'user_123', 6 }); 7 8 const connectedAccount = response.connectedAccount; 9 // ACTIVE: ready for tool calls 10 // PENDING: user has not completed the OAuth flow 11 // EXPIRED: tokens expired, re-authorization required 12 // REVOKED: user revoked access from the provider 13 14 if (connectedAccount?.status !== ConnectorStatus.ACTIVE) { 15 const linkResponse = await actions.getAuthorizationLink({ 16 connectionName: 'gmail', 17 identifier: 'user_123', 18 }); 19 // Redirect or send linkResponse.link to the user 20 } ```

---
# DOCUMENT BOUNDARY
---

# Pre and Post Processors

> Learn how to create pre and post processor workflows that are run before or after tool execution with Agent Auth.

Custom pre and post processors are a way to create custom workflows that are run before or after tool execution with Agent Auth. They are useful for: * Validating and transforming input data * Processing and Formatting output data * Adding additional context to the tool execution ## Usage [Section titled “Usage”](#usage)

---
# DOCUMENT BOUNDARY
---

# Custom tools

> Build tools that Scalekit does not provide out of the box by proxying provider API calls through connected accounts.

When you need a connector tool that Scalekit doesn’t offer as a pre-built tool, use **API Proxy mode**. You define the tool contract and call the provider endpoint through `actions.request`. Scalekit injects the user’s credentials from their connected account; your agent never handles raw tokens. | Option | Best for | Who defines tool schema | | ------------------------ | --------------------------------- | ----------------------- | | Scalekit optimized tools | Common connector tools | Scalekit | | Custom tools (API Proxy) | Unsupported or app-specific tools | Your application | This page assumes the user has an `ACTIVE` connected account. If not, see [Authorize a user](/agentkit/tools/authorize/). ## Find the right endpoint [Section titled “Find the right endpoint”](#find-the-right-endpoint) The `path` you pass to `actions.request` is forwarded directly to the provider’s API; Scalekit only adds authentication headers. Look up the provider’s API reference to get the correct path, method, and request shape. | Connector | API reference | | ---------- | ------------------------------------------------------------------------------------------------ | | Gmail | [Google Gmail API](https://developers.google.com/gmail/api/reference/rest) | | Slack | [Slack API methods](https://api.slack.com/methods) | | GitHub | [GitHub REST API](https://docs.github.com/en/rest) | | Salesforce | [Salesforce REST API](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/) | | HubSpot | [HubSpot API](https://developers.hubspot.com/docs/api/overview) | ## Define your tool contract [Section titled “Define your tool contract”](#define-your-tool-contract) Design the tool around your agent’s intent, not the provider’s API surface. For example, to list Gmail filters: * **Tool name:** `gmail_list_filters` (describes the action, not the endpoint) * **Input:** `identifier` (your app’s user ID) * **Output:** `{ filters: [...], count: N }` (structured, not the raw Gmail response) Keep schemas focused on what the model needs. Strip provider-specific noise before returning data. ## Proxy the API call [Section titled “Proxy the API call”](#proxy-the-api-call) Use `actions.request` to call any provider endpoint. Scalekit handles credential injection. **GET requests:** pass query parameters as a dict: * Python ```python 1 def gmail_list_filters(identifier: str): 2 response = actions.request( 3 connection_name="gmail", 4 identifier=identifier, 5 method="GET", 6 path="/gmail/v1/users/me/settings/filters", 7 ) 8 data = response.json() 9 return {"filters": data.get("filter", []), "count": len(data.get("filter", []))} 10 11 def gmail_list_unread(identifier: str, max_results: int = 10): 12 response = actions.request( 13 connection_name="gmail", 14 identifier=identifier, 15 method="GET", 16 path="/gmail/v1/users/me/messages", 17 query_params={"q": "is:unread", "maxResults": max_results}, 18 ) 19 return {"messages": response.json().get("messages", [])} ``` * Node.js ```typescript 1 async function gmailListFilters(identifier: string) { 2 const response = await scalekit.actions.request({ 3 connectionName: 'gmail', 4 identifier, 5 method: 'GET', 6 path: '/gmail/v1/users/me/settings/filters', 7 }); 8 const filters = response.data?.filter ?? []; 9 return { filters, count: filters.length }; 10 } 11 12 async function gmailListUnread(identifier: string, maxResults = 10) { 13 const response = await scalekit.actions.request({ 14 connectionName: 'gmail', 15 identifier, 16 method: 'GET', 17 path: '/gmail/v1/users/me/messages', 18 queryParams: { q: 'is:unread', maxResults }, 19 }); 20 return { messages: response.data?.messages ?? [] }; 21 } ``` **POST requests:** pass a body for write operations: * Python ```python 1 def slack_send_message(identifier: str, channel: str, text: str): 2 response = actions.request( 3 connection_name="slack", 4 identifier=identifier, 5 method="POST", 6 path="/api/chat.postMessage", 7 body={"channel": channel, "text": text}, 8 ) 9 data = response.json() 10 if not data.get("ok"): 11 raise ValueError(f"Slack error: {data.get('error')}") 12 return {"ts": data.get("ts"), "channel": data.get("channel")} ``` * Node.js ```typescript 1 async function slackSendMessage(identifier: string, channel: string, text: string) { 2 const response = await scalekit.actions.request({ 3 connectionName: 'slack', 4 identifier, 5 method: 'POST', 6 path: '/api/chat.postMessage', 7 body: { channel, text }, 8 }); 9 if (!response.data?.ok) throw new Error(`Slack error: ${response.data?.error}`); 10 return { ts: response.data.ts, channel: response.data.channel }; 11 } ``` ## Check authorization before proxy calls [Section titled “Check authorization before proxy calls”](#check-authorization-before-proxy-calls) Verify the connected account is `ACTIVE` before making a proxy call and handle provider errors explicitly: * Python ```python 1 account = actions.get_or_create_connected_account( 2 connection_name="gmail", 3 identifier=identifier, 4 ).connected_account 5 6 if account.status != "ACTIVE": 7 raise ValueError("Connected account is not ACTIVE. Re-authorize the user.") ``` * Node.js ```typescript 1 import { ConnectorStatus } from '@scalekit-sdk/node/lib/pkg/grpc/scalekit/v1/connected_accounts/connected_accounts_pb'; 2 3 const account = (await scalekit.actions.getOrCreateConnectedAccount({ 4 connectionName: 'gmail', 5 identifier, 6 })).connectedAccount; 7 8 if (account?.status !== ConnectorStatus.ACTIVE) { 9 throw new Error('Connected account is not ACTIVE. Re-authorize the user.'); 10 } ``` ## Best practices [Section titled “Best practices”](#best-practices) * Expose only the fields your model needs; keep schemas small * Validate inputs server-side; never trust model-generated parameters * Use predictable JSON keys; return stable output across calls * Map provider errors to clear tool errors; don’t leak raw provider payloads to prompts

---
# DOCUMENT BOUNDARY
---

# Proxy Tools

> Learn how to make direct API calls to providers using Agent Auth's proxy tools.

Custom tool definitions allow you to create specialized tools tailored to your specific business needs. You can combine multiple provider tools, add custom logic, and create reusable workflows that go beyond standard tool functionality. ## What are custom tools? [Section titled “What are custom tools?”](#what-are-custom-tools) Custom tools are user-defined functions that: * **Extend existing tools**: Build on top of standard provider tools * **Combine multiple operations**: Create workflows that use multiple tools * **Add business logic**: Include custom validation, processing, and formatting * **Create reusable patterns**: Standardize common operations across your team * **Integrate with external systems**: Connect to your own APIs and services ## Custom tool structure [Section titled “Custom tool structure”](#custom-tool-structure) Every custom tool follows a standardized structure: ```javascript 1 { 2 name: 'custom_tool_name', 3 display_name: 'Custom Tool Display Name', 4 description: 'Description of what the tool does', 5 category: 'custom', 6 provider: 'custom', 7 input_schema: { 8 type: 'object', 9 properties: { 10 // Define input parameters 11 }, 12 required: ['required_param'] 13 }, 14 output_schema: { 15 type: 'object', 16 properties: { 17 // Define output format 18 } 19 }, 20 implementation: async (parameters, context) => { 21 // Custom tool logic 22 return result; 23 } 24 } ``` ## Creating custom tools [Section titled “Creating custom tools”](#creating-custom-tools) ### Basic custom tool [Section titled “Basic custom tool”](#basic-custom-tool) Here’s a simple custom tool that sends a welcome email: ```javascript 1 const sendWelcomeEmail = { 2 name: 'send_welcome_email', 3 display_name: 'Send Welcome Email', 4 description: 'Send a personalized welcome email to new users', 5 category: 'communication', 6 provider: 'custom', 7 input_schema: { 8 type: 'object', 9 properties: { 10 user_name: { 11 type: 'string', 12 description: 'Name of the new user' 13 }, 14 user_email: { 15 type: 'string', 16 format: 'email', 17 description: 'Email address of the new user' 18 }, 19 company_name: { 20 type: 'string', 21 description: 'Name of the company' 22 } 23 }, 24 required: ['user_name', 'user_email', 'company_name'] 25 }, 26 output_schema: { 27 type: 'object', 28 properties: { 29 message_id: { 30 type: 'string', 31 description: 'ID of the sent email' 32 }, 33 status: { 34 type: 'string', 35 enum: ['sent', 'failed'], 36 description: 'Status of the email' 37 } 38 } 39 }, 40 implementation: async (parameters, context) => { 41 const { user_name, user_email, company_name } = parameters; 42 43 // Generate personalized email content 44 const emailBody = ` 45 Welcome to ${company_name}, ${user_name}! 46 47 We're excited to have you join our team. Here are some next steps: 48 49 1. Complete your profile setup 50 2. Join our Slack workspace 51 3. Schedule a meeting with your manager 52 53 If you have any questions, don't hesitate to reach out! 54 55 Best regards, 56 The ${company_name} Team 57 `; 58 59 // Send email using standard email tool 60 const result = await context.tools.execute({ 61 tool: 'send_email', 62 parameters: { 63 to: [user_email], 64 subject: `Welcome to ${company_name}!`, 65 body: emailBody 66 } 67 }); 68 69 return { 70 message_id: result.message_id, 71 status: result.status === 'sent' ? 'sent' : 'failed' 72 }; 73 } 74 }; ``` ### Multi-step workflow tool [Section titled “Multi-step workflow tool”](#multi-step-workflow-tool) Create a tool that combines multiple operations: ```javascript 1 const createProjectWorkflow = { 2 name: 'create_project_workflow', 3 display_name: 'Create Project Workflow', 4 description: 'Create a complete project setup with Jira project, Slack channel, and team notifications', 5 category: 'project_management', 6 provider: 'custom', 7 input_schema: { 8 type: 'object', 9 properties: { 10 project_name: { 11 type: 'string', 12 description: 'Name of the project' 13 }, 14 project_key: { 15 type: 'string', 16 description: 'Project key for Jira' 17 }, 18 team_members: { 19 type: 'array', 20 items: { type: 'string', format: 'email' }, 21 description: 'Team member email addresses' 22 }, 23 project_description: { 24 type: 'string', 25 description: 'Project description' 26 } 27 }, 28 required: ['project_name', 'project_key', 'team_members'] 29 }, 30 output_schema: { 31 type: 'object', 32 properties: { 33 jira_project_id: { type: 'string' }, 34 slack_channel_id: { type: 'string' }, 35 notifications_sent: { type: 'number' } 36 } 37 }, 38 implementation: async (parameters, context) => { 39 const { project_name, project_key, team_members, project_description } = parameters; 40 41 try { 42 // Step 1: Create Jira project 43 const jiraProject = await context.tools.execute({ 44 tool: 'create_jira_project', 45 parameters: { 46 key: project_key, 47 name: project_name, 48 description: project_description, 49 project_type: 'software' 50 } 51 }); 52 53 // Step 2: Create Slack channel 54 const slackChannel = await context.tools.execute({ 55 tool: 'create_channel', 56 parameters: { 57 name: `${project_key.toLowerCase()}-team`, 58 topic: `Discussion for ${project_name}`, 59 is_private: false 60 } 61 }); 62 63 // Step 3: Send notifications to team members 64 let notificationCount = 0; 65 for (const member of team_members) { 66 try { 67 await context.tools.execute({ 68 tool: 'send_email', 69 parameters: { 70 to: [member], 71 subject: `New Project: ${project_name}`, 72 body: ` 73 You've been added to the new project "${project_name}". 74 75 Jira Project: ${jiraProject.project_url} 76 Slack Channel: #${slackChannel.channel_name} 77 78 Please join the Slack channel to start collaborating! 79 ` 80 } 81 }); 82 notificationCount++; 83 } catch (error) { 84 console.error(`Failed to send notification to ${member}:`, error); 85 } 86 } 87 88 // Step 4: Post welcome message to Slack channel 89 await context.tools.execute({ 90 tool: 'send_message', 91 parameters: { 92 channel: `#${slackChannel.channel_name}`, 93 text: `<� Welcome to ${project_name}! This channel is for project discussion and updates.` 94 } 95 }); 96 97 return { 98 jira_project_id: jiraProject.project_id, 99 slack_channel_id: slackChannel.channel_id, 100 notifications_sent: notificationCount 101 }; 102 103 } catch (error) { 104 throw new Error(`Project creation failed: ${error.message}`); 105 } 106 } 107 }; ``` ### Data processing tool [Section titled “Data processing tool”](#data-processing-tool) Create a tool that processes and analyzes data: ```javascript 1 const generateTeamReport = { 2 name: 'generate_team_report', 3 display_name: 'Generate Team Report', 4 description: 'Generate a comprehensive team performance report from multiple sources', 5 category: 'analytics', 6 provider: 'custom', 7 input_schema: { 8 type: 'object', 9 properties: { 10 team_members: { 11 type: 'array', 12 items: { type: 'string', format: 'email' }, 13 description: 'Team member email addresses' 14 }, 15 start_date: { 16 type: 'string', 17 format: 'date', 18 description: 'Report start date' 19 }, 20 end_date: { 21 type: 'string', 22 format: 'date', 23 description: 'Report end date' 24 }, 25 include_calendar: { 26 type: 'boolean', 27 default: true, 28 description: 'Include calendar analysis' 29 } 30 }, 31 required: ['team_members', 'start_date', 'end_date'] 32 }, 33 output_schema: { 34 type: 'object', 35 properties: { 36 report_url: { type: 'string' }, 37 summary: { type: 'object' }, 38 sent_to: { type: 'array', items: { type: 'string' } } 39 } 40 }, 41 implementation: async (parameters, context) => { 42 const { team_members, start_date, end_date, include_calendar } = parameters; 43 44 // Fetch Jira issues assigned to team members 45 const jiraIssues = await context.tools.execute({ 46 tool: 'fetch_issues', 47 parameters: { 48 jql: `assignee in (${team_members.join(',')}) AND created >= ${start_date} AND created <= ${end_date}`, 49 fields: ['summary', 'status', 'assignee', 'created', 'resolved'] 50 } 51 }); 52 53 // Fetch calendar events if requested 54 let calendarData = null; 55 if (include_calendar) { 56 calendarData = await context.tools.execute({ 57 tool: 'fetch_events', 58 parameters: { 59 start_date: start_date, 60 end_date: end_date, 61 attendees: team_members 62 } 63 }); 64 } 65 66 // Process and analyze data 67 const report = { 68 period: { start_date, end_date }, 69 team_size: team_members.length, 70 issues: { 71 total: jiraIssues.issues.length, 72 completed: jiraIssues.issues.filter(i => i.status === 'Done').length, 73 in_progress: jiraIssues.issues.filter(i => i.status === 'In Progress').length 74 }, 75 meetings: calendarData ? { 76 total: calendarData.events.length, 77 hours: calendarData.events.reduce((acc, event) => acc + event.duration, 0) 78 } : null 79 }; 80 81 // Generate HTML report 82 const htmlReport = ` 83  84 Team Report - ${start_date} to ${end_date} 85  86 

Team Performance Report

87

Summary

88

Team Size: ${report.team_size}

89

Total Issues: ${report.issues.total}

90

Completed Issues: ${report.issues.completed}

91

In Progress: ${report.issues.in_progress}

92 ${report.meetings ? `

Total Meetings: ${report.meetings.total}

` : ''} 93 94 95 `; 96 97 // Send report via email 98 const emailResults = await Promise.all( 99 team_members.map(member => 100 context.tools.execute({ 101 tool: 'send_email', 102 parameters: { 103 to: [member], 104 subject: `Team Report - ${start_date} to ${end_date}`, 105 html_body: htmlReport 106 } 107 }) 108 ) 109 ); 110 111 return { 112 report_url: 'Generated and sent via email', 113 summary: report, 114 sent_to: team_members.filter((_, index) => emailResults[index].status === 'sent') 115 }; 116 } 117 }; ``` ## Registering custom tools [Section titled “Registering custom tools”](#registering-custom-tools) ### Using the API [Section titled “Using the API”](#using-the-api) Register your custom tools with Agent Auth: * JavaScript ```javascript 1 // Register a custom tool 2 const registeredTool = await agentConnect.tools.register({ 3 ...sendWelcomeEmail, 4 organization_id: 'your_org_id' 5 }); 6 7 console.log('Tool registered:', registeredTool.id); ``` * Python ```python 1 # Register a custom tool 2 registered_tool = agent_connect.tools.register( 3 **send_welcome_email, 4 organization_id='your_org_id' 5 ) 6 7 print(f'Tool registered: {registered_tool.id}') ``` * cURL ```bash 1 curl -X POST "${SCALEKIT_BASE_URL}/v1/connect/tools/custom" \ 2 -H "Authorization: Bearer ${SCALEKIT_CLIENT_SECRET}" \ 3 -H "Content-Type: application/json" \ 4 -d '{ 5 "name": "send_welcome_email", 6 "display_name": "Send Welcome Email", 7 "description": "Send a personalized welcome email to new users", 8 "category": "communication", 9 "provider": "custom", 10 "input_schema": {...}, 11 "output_schema": {...}, 12 "implementation": "async (parameters, context) => {...}" 13 }' ``` ### Using the dashboard [Section titled “Using the dashboard”](#using-the-dashboard) 1. In the [Scalekit dashboard](https://app.scalekit.com), go to **AgentKit** > **Tools** 2. Click **Create Custom Tool** 3. Fill in the tool definition form 4. Test the tool with sample parameters 5. Save and activate the tool ## Tool context and utilities [Section titled “Tool context and utilities”](#tool-context-and-utilities) The `context` object provides access to: ### Standard tools [Section titled “Standard tools”](#standard-tools) Execute any standard Agent Auth tool: ```javascript 1 // Execute standard tools 2 const result = await context.tools.execute({ 3 tool: 'send_email', 4 parameters: { ... } 5 }); 6 7 // Execute with specific connected account 8 const result = await context.tools.execute({ 9 connected_account_id: 'specific_account', 10 tool: 'send_email', 11 parameters: { ... } 12 }); ``` ### Connected accounts [Section titled “Connected accounts”](#connected-accounts) Access connected account information: ```javascript 1 // Get connected account details 2 const account = await context.accounts.get(accountId); 3 4 // List accounts for a user 5 const accounts = await context.accounts.list({ 6 identifier: 'user_123', 7 provider: 'gmail' 8 }); ``` ### Utilities [Section titled “Utilities”](#utilities) Access utility functions: ```javascript 1 // Generate unique IDs 2 const id = context.utils.generateId(); 3 4 // Format dates 5 const formatted = context.utils.formatDate(date, 'YYYY-MM-DD'); 6 7 // Validate email 8 const isValid = context.utils.isValidEmail(email); 9 10 // HTTP requests 11 const response = await context.utils.httpRequest({ 12 url: 'https://api.example.com/data', 13 method: 'GET', 14 headers: { 'Authorization': 'Bearer token' } 15 }); ``` ### Error handling [Section titled “Error handling”](#error-handling) Throw structured errors: ```javascript 1 // Throw validation error 2 throw new context.errors.ValidationError('Invalid email format'); 3 4 // Throw business logic error 5 throw new context.errors.BusinessLogicError('User not found'); 6 7 // Throw external API error 8 throw new context.errors.ExternalAPIError('GitHub API returned 500'); ``` ## Testing custom tools [Section titled “Testing custom tools”](#testing-custom-tools) ### Unit testing [Section titled “Unit testing”](#unit-testing) Test custom tools in isolation: ```javascript 1 // Mock context for testing 2 const mockContext = { 3 tools: { 4 execute: jest.fn().mockResolvedValue({ 5 message_id: 'test_msg_123', 6 status: 'sent' 7 }) 8 }, 9 utils: { 10 generateId: () => 'test_id_123', 11 formatDate: (date, format) => '2024-01-15' 12 } 13 }; 14 15 // Test custom tool 16 const result = await sendWelcomeEmail.implementation({ 17 user_name: 'John Doe', 18 user_email: 'john@example.com', 19 company_name: 'Acme Corp' 20 }, mockContext); 21 22 expect(result.status).toBe('sent'); 23 expect(mockContext.tools.execute).toHaveBeenCalledWith({ 24 tool: 'send_email', 25 parameters: expect.objectContaining({ 26 to: ['john@example.com'], 27 subject: 'Welcome to Acme Corp!' 28 }) 29 }); ``` ### Integration testing [Section titled “Integration testing”](#integration-testing) Test with real Agent Auth: ```javascript 1 // Test custom tool with real connections 2 const testResult = await agentConnect.tools.execute({ 3 connected_account_id: 'test_gmail_account', 4 tool: 'send_welcome_email', 5 parameters: { 6 user_name: 'Test User', 7 user_email: 'test@example.com', 8 company_name: 'Test Company' 9 } 10 }); 11 12 console.log('Test result:', testResult); ``` ## Best practices [Section titled “Best practices”](#best-practices) ### Tool design [Section titled “Tool design”](#tool-design) * **Single responsibility**: Each tool should have a clear, single purpose * **Consistent naming**: Use descriptive, consistent naming conventions * **Clear documentation**: Provide detailed descriptions and examples * **Error handling**: Implement comprehensive error handling * **Input validation**: Validate all input parameters ### Performance optimization [Section titled “Performance optimization”](#performance-optimization) * **Parallel execution**: Use Promise.all() for independent operations * **Caching**: Cache frequently accessed data * **Batch operations**: Group similar operations together * **Timeout handling**: Set appropriate timeouts for external calls ### Security considerations [Section titled “Security considerations”](#security-considerations) * **Input sanitization**: Sanitize all user inputs * **Permission checks**: Verify user permissions before execution * **Sensitive data**: Handle sensitive data securely * **Rate limiting**: Implement rate limiting for resource-intensive operations ## Custom tool examples [Section titled “Custom tool examples”](#custom-tool-examples) ### Slack notification tool [Section titled “Slack notification tool”](#slack-notification-tool) ```javascript 1 const sendSlackNotification = { 2 name: 'send_slack_notification', 3 display_name: 'Send Slack Notification', 4 description: 'Send formatted notifications to Slack with optional mentions', 5 category: 'communication', 6 provider: 'custom', 7 input_schema: { 8 type: 'object', 9 properties: { 10 channel: { type: 'string' }, 11 message: { type: 'string' }, 12 severity: { type: 'string', enum: ['info', 'warning', 'error'] }, 13 mentions: { type: 'array', items: { type: 'string' } } 14 }, 15 required: ['channel', 'message'] 16 }, 17 output_schema: { 18 type: 'object', 19 properties: { 20 message_ts: { type: 'string' }, 21 permalink: { type: 'string' } 22 } 23 }, 24 implementation: async (parameters, context) => { 25 const { channel, message, severity = 'info', mentions = [] } = parameters; 26 27 const colors = { 28 info: 'good', 29 warning: 'warning', 30 error: 'danger' 31 }; 32 33 const mentionText = mentions.length > 0 ? 34 `${mentions.map(m => `<@${m}>`).join(' ')} ` : ''; 35 36 return await context.tools.execute({ 37 tool: 'send_message', 38 parameters: { 39 channel, 40 text: `${mentionText}${message}`, 41 attachments: [ 42 { 43 color: colors[severity], 44 text: message, 45 ts: Math.floor(Date.now() / 1000) 46 } 47 ] 48 } 49 }); 50 } 51 }; ``` ### Calendar scheduling tool [Section titled “Calendar scheduling tool”](#calendar-scheduling-tool) ```javascript 1 const scheduleTeamMeeting = { 2 name: 'schedule_team_meeting', 3 display_name: 'Schedule Team Meeting', 4 description: 'Find available time slots and schedule team meetings', 5 category: 'scheduling', 6 provider: 'custom', 7 input_schema: { 8 type: 'object', 9 properties: { 10 attendees: { type: 'array', items: { type: 'string' } }, 11 duration: { type: 'number', minimum: 15 }, 12 preferred_times: { type: 'array', items: { type: 'string' } }, 13 meeting_title: { type: 'string' }, 14 meeting_description: { type: 'string' } 15 }, 16 required: ['attendees', 'duration', 'meeting_title'] 17 }, 18 output_schema: { 19 type: 'object', 20 properties: { 21 event_id: { type: 'string' }, 22 scheduled_time: { type: 'string' }, 23 attendees_notified: { type: 'number' } 24 } 25 }, 26 implementation: async (parameters, context) => { 27 const { attendees, duration, preferred_times, meeting_title, meeting_description } = parameters; 28 29 // Find available time slots 30 const availableSlots = await context.tools.execute({ 31 tool: 'find_available_slots', 32 parameters: { 33 attendees, 34 duration, 35 preferred_times: preferred_times || [] 36 } 37 }); 38 39 if (availableSlots.length === 0) { 40 throw new context.errors.BusinessLogicError('No available time slots found'); 41 } 42 43 // Schedule the meeting at the first available slot 44 const selectedSlot = availableSlots[0]; 45 const event = await context.tools.execute({ 46 tool: 'create_event', 47 parameters: { 48 title: meeting_title, 49 description: meeting_description, 50 start_time: selectedSlot.start_time, 51 end_time: selectedSlot.end_time, 52 attendees 53 } 54 }); 55 56 return { 57 event_id: event.event_id, 58 scheduled_time: selectedSlot.start_time, 59 attendees_notified: attendees.length 60 }; 61 } 62 }; ``` ## Versioning and deployment [Section titled “Versioning and deployment”](#versioning-and-deployment) ### Version management [Section titled “Version management”](#version-management) Version your custom tools for backward compatibility: ```javascript 1 const toolV2 = { 2 ...originalTool, 3 version: '2.0.0', 4 // Updated implementation 5 }; 6 7 // Deploy new version 8 await agentConnect.tools.register(toolV2); 9 10 // Deprecate old version 11 await agentConnect.tools.deprecate(originalTool.name, '1.0.0'); ``` ### Deployment strategies [Section titled “Deployment strategies”](#deployment-strategies) * **Blue-green deployment**: Deploy new version alongside old version * **Canary deployment**: Gradually roll out to subset of users * **Feature flags**: Use feature flags to control tool availability * **Rollback strategy**: Plan for quick rollback if issues arise Custom tools unlock the full potential of Agent Auth by allowing you to create specialized workflows that perfectly match your business needs. With proper design, testing, and deployment practices, you can build powerful tools that enhance your team’s productivity and streamline complex operations. --- # DOCUMENT BOUNDARY --- # Scalekit optimized built-in tools > Call Scalekit's pre-built tools across 100+ connectors. Each tool returns structured, LLM-ready output with no endpoint URLs, auth headers, or parsing needed. Scalekit ships pre-built tools for every connector in the catalog: Gmail, Slack, GitHub, Salesforce, Notion, Linear, HubSpot, and more. Each tool has an LLM-ready schema and returns structured output. Your agent passes inputs; Scalekit injects the user’s credentials and handles the API call. This page assumes you have an `ACTIVE` connected account for the user. If not, see [Authorize a user](/agentkit/tools/authorize/). ## Get available tools for a user [Section titled “Get available tools for a user”](#get-available-tools-for-a-user) Use `list_scoped_tools` / `listScopedTools` to get the tools this specific user is authorized to call. **This is the list you pass to your LLM.** * Python ```python 1 from google.protobuf.json_format import MessageToDict 2 3 scoped_response, _ = actions.tools.list_scoped_tools( 4 identifier="user_123", 5 filter={"connection_names": ["gmail"]}, # optional; omit for all connectors 6 page_size=100, # fetch beyond the default page 7 ) 8 for scoped_tool in scoped_response.tools: 9 definition = MessageToDict(scoped_tool.tool).get("definition", {}) 10 print(definition.get("name")) 11 print(definition.get("input_schema")) # JSON Schema; pass directly to your LLM ``` * Node.js ```typescript 1 const { tools } = await scalekit.tools.listScopedTools('user_123', { 2 filter: { connectionNames: ['gmail'] }, // use filter: {} to list every connector 3 pageSize: 100, // fetch beyond the default page 4 }); 5 for (const tool of tools) { 6 const { name, input_schema } = tool.tool.definition; 7 console.log(name, input_schema); // JSON Schema; pass directly to your LLM 8 } ``` To explore tools interactively, use the playground at [**Scalekit Dashboard**](https://app.scalekit.com) **> AgentKit > Playground**. ## Execute a tool [Section titled “Execute a tool”](#execute-a-tool) Use `execute_tool` / `executeTool` to run a named tool for a specific user. Scalekit identifies the connected account with: * User identifier (`identifier`) + Connection name as shown in the Scalekit Dashboard (`connection_name`), or * Connected Account ID (`connected_account_id`) — autogenerated by Scalekit and visible in the Scalekit Dashboard - Python ```python 1 # connected account is selected using the user identifier and the connection name 2 result = actions.execute_tool( 3 tool_name="gmail_fetch_mails", 4 identifier="user_123", 5 connection_name="gmail", 6 tool_input={"query": "is:unread", "max_results": 5}, 7 ) 8 print(result.data) 9 10 # alternatively, use the connected account ID 11 # result = actions.execute_tool( 12 # tool_name="gmail_fetch_mails", 13 # connected_account_id="ca_xxxxxx", 14 # tool_input={"query": "is:unread", "max_results": 5}, 15 # ) ``` - Node.js ```typescript 1 // connected account is selected using the user identifier and the connector 2 const result = await scalekit.actions.executeTool({ 3 toolName: 'gmail_fetch_mails', 4 identifier: 'user_123', 5 connector: 'gmail', 6 toolInput: { query: 'is:unread', max_results: 5 }, 7 }); 8 console.log(result.data); 9 10 // alternatively, use the connected account ID 11 // const result = await scalekit.actions.executeTool({ 12 // toolName: 'gmail_fetch_mails', 13 // connectedAccountId: 'ca_xxxxxx', 14 // toolInput: { query: 'is:unread', max_results: 5 }, 15 // }); ``` ## Wire into your LLM [Section titled “Wire into your LLM”](#wire-into-your-llm) The full agent loop: fetch scoped tools → pass to LLM → execute tool calls → feed results back. * Python ```python 1 import anthropic 2 from google.protobuf.json_format import MessageToDict 3 4 client = anthropic.Anthropic() 5 6 # 1. Fetch tools scoped to this user 7 scoped_response, _ = actions.tools.list_scoped_tools( 8 identifier="user_123", 9 filter={"connection_names": ["gmail"]}, 10 page_size=100, # fetch beyond the default page so no connector tools are missed 11 ) 12 llm_tools = [ 13 { 14 "name": MessageToDict(t.tool).get("definition", {}).get("name"), 15 "description": MessageToDict(t.tool).get("definition", {}).get("description"), 16 "input_schema": MessageToDict(t.tool).get("definition", {}).get("input_schema", {}), 17 } 18 for t in scoped_response.tools 19 ] 20 21 # 2. Send to LLM 22 messages = [{"role": "user", "content": "Summarize my last 5 unread emails"}] 23 response = client.messages.create( 24 model="claude-sonnet-4-6", 25 max_tokens=1024, 26 tools=llm_tools, 27 messages=messages, 28 ) 29 30 # 3. Execute tool calls and feed results back 31 for block in response.content: 32 if block.type == "tool_use": 33 tool_result = actions.execute_tool( 34 tool_name=block.name, 35 identifier="user_123", 36 tool_input=block.input, 37 ) 38 messages.append({"role": "assistant", "content": response.content}) 39 messages.append({ 40 "role": "user", 41 "content": [{"type": "tool_result", "tool_use_id": block.id, "content": str(tool_result.data)}], 42 }) ``` * Node.js ```typescript 1 import Anthropic from '@anthropic-ai/sdk'; 2 3 const anthropic = new Anthropic(); 4 5 // 1. Fetch tools scoped to this user 6 const { tools } = await scalekit.tools.listScopedTools('user_123', { 7 filter: { connectionNames: ['gmail'] }, 8 pageSize: 100, // fetch beyond the default page so no connector tools are missed 9 }); 10 const llmTools = tools.map((t) => ({ 11 name: t.tool.definition.name, 12 description: t.tool.definition.description, 13 input_schema: t.tool.definition.input_schema, 14 })); 15 16 // 2. Send to LLM 17 const messages: Anthropic.MessageParam[] = [ 18 { role: 'user', content: 'Summarize my last 5 unread emails' }, 19 ]; 20 const response = await anthropic.messages.create({ 21 model: 'claude-sonnet-4-6', 22 max_tokens: 1024, 23 tools: llmTools, 24 messages, 25 }); 26 27 // 3. Execute tool calls and feed results back 28 for (const block of response.content) { 29 if (block.type === 'tool_use') { 30 const toolResult = await scalekit.actions.executeTool({ 31 toolName: block.name, 32 identifier: 'user_123', 33 toolInput: block.input as Record, 34 }); 35 messages.push({ role: 'assistant', content: response.content }); 36 messages.push({ 37 role: 'user', 38 content: [{ type: 'tool_result', tool_use_id: block.id, content: JSON.stringify(toolResult.data) }], 39 }); 40 } 41 } ``` ## Use a framework adapter [Section titled “Use a framework adapter”](#use-a-framework-adapter) For LangChain and Google ADK, Scalekit returns native tool objects in Python with no schema reshaping needed. * LangChain ```python 1 from langchain_openai import ChatOpenAI 2 from langchain.agents import create_agent 3 4 tools = actions.langchain.get_tools( 5 identifier="user_123", 6 connection_names=["gmail"], 7 page_size=100, # avoid missing tools when a connector has more than the default page 8 ) 9 llm = ChatOpenAI(model="claude-sonnet-4-6") 10 agent = create_agent(model=llm, tools=tools, system_prompt="You are a helpful assistant.") 11 result = agent.invoke({"messages": [{"role": "user", "content": "Fetch my last 5 unread emails"}]}) ``` * Google ADK ```python 1 from google.adk.agents import Agent 2 from google.adk.models.lite_llm import LiteLlm 3 4 gmail_tools = actions.google.get_tools( 5 identifier="user_123", 6 connection_names=["gmail"], 7 page_size=100, # avoid missing tools when a connector has more than the default page 8 ) 9 agent = Agent( 10 name="gmail_assistant", 11 model=LiteLlm(model="claude-sonnet-4-6"), 12 tools=gmail_tools, 13 ) ``` * Node.js (Vercel AI SDK) ```typescript 1 import { generateText, jsonSchema, tool } from 'ai'; 2 3 const { tools: scopedTools } = await scalekit.tools.listScopedTools('user_123', { 4 filter: { connectionNames: ['gmail'] }, 5 pageSize: 100, // fetch beyond the default page so no connector tools are missed 6 }); 7 const tools = Object.fromEntries( 8 scopedTools.map((t) => [ 9 t.tool.definition.name, 10 tool({ 11 description: t.tool.definition.description, 12 parameters: jsonSchema(t.tool.definition.input_schema ?? { type: 'object', properties: {} }), 13 execute: async (args) => { 14 const result = await scalekit.actions.executeTool({ 15 toolName: t.tool.definition.name, 16 toolInput: args, 17 identifier: 'user_123', 18 }); 19 return result.data; 20 }, 21 }), 22 ]), 23 ); ``` ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) If you need an endpoint not covered by optimized tools, see [Custom tools](/agentkit/tools/custom-tools/). --- # DOCUMENT BOUNDARY --- # Verify user identity > Confirm that the user who completed the OAuth consent is the same user your app intended to connect. User verification applies to OAuth-based connectors only. For API key, basic auth, and key pair connectors, the user provides credentials directly. No OAuth flow, no verification step needed. For OAuth connectors, before activating a connected account, Scalekit confirms that the user who completed the OAuth consent is the same user your app intended to connect. This **user verification** step runs every time a connected account is authorized and prevents OAuth consent from activating on the wrong account. Choose a mode in **AgentKit** > **User Verification**: * **Custom user verification**: Your server confirms the authorizing user matches the user your app intended to connect. Use in production. Without this, any user who receives an authorization link can activate a connected account (including the wrong one). * **Scalekit users only**: Scalekit checks that the authorizing user is signed in to your Scalekit dashboard. No code required. Use during development and internal testing when all users are already on your team. ![AgentKit User Verification showing Custom user verifier and Scalekit users only](/.netlify/images?url=_astro%2Fuser-verification-config.R9EpQz_E.png\&w=2224\&h=1590\&dpl=6a01bf5aba8408000850fe26) Your application implements the verify step. End users never interact with Scalekit directly. When the user finishes OAuth, Scalekit redirects to your verify URL with `auth_request_id` and `state` params. Your route reads the user from your session, calls Scalekit’s verify API with the `auth_request_id` and the original `identifier`, and if they match, the connected account activates. ## Implement verification in your app [Section titled “Implement verification in your app”](#implement-verification-in-your-app) If you haven’t installed the SDK yet, see the [quickstart](/agentkit/quickstart/). ### Generate the authorization link [Section titled “Generate the authorization link”](#generate-the-authorization-link) Pass these fields when creating the authorization link: | Field | Description | | ----------------- | ------------------------------------------------------------------------------------------------- | | `identifier` | **Required.** Your user’s ID or email. Scalekit stores this and checks it matches at verify time. | | `user_verify_url` | **Required.** Your callback URL; Scalekit redirects the user here after OAuth completes. | | `state` | **Recommended.** A random value to prevent CSRF. | * Python ```python 1 import secrets 2 3 # Generate a state value to prevent CSRF 4 state = secrets.token_urlsafe(32) 5 # Store state in a secure, HTTP-only cookie to validate on callback 6 7 response = scalekit_client.actions.get_authorization_link( 8 connection_name=connector, 9 identifier=user_id, 10 user_verify_url="https://app.yourapp.com/user/verify", 11 state=state, 12 ) ``` * Node.js ```typescript 1 import crypto from 'node:crypto'; 2 3 // Generate a state value to prevent CSRF 4 const state = crypto.randomUUID(); 5 // Store state in a secure, HTTP-only cookie to validate on callback 6 7 const { link } = await scalekit.actions.getAuthorizationLink({ 8 identifier: userId, 9 connectionName: connector, 10 userVerifyUrl: 'https://app.yourapp.com/user/verify', 11 state, 12 }); ``` ### Handle the verification callback [Section titled “Handle the verification callback”](#handle-the-verification-callback) After OAuth completes, Scalekit redirects to your `user_verify_url`: ```http 1 GET https://app.yourapp.com/user/verify?auth_request_id=req_xyz&state= ``` Validate `state` against your cookie, then call Scalekit’s verify endpoint server-side. Never trust query params for identity Read the user’s identity from your own session, not from the URL. Use `state` for session correlation only. * Python ```python 1 # 1. Validate state from query param matches state in cookie 2 # 2. Read user identity from your session, not from the URL 3 4 response = scalekit_client.actions.verify_connected_account_user( 5 auth_request_id=auth_request_id, 6 identifier=user_id, # must match what was stored at link creation 7 ) 8 # On success: redirect to response.post_user_verify_redirect_url ``` * Node.js ```typescript 1 // 1. Validate state from query param matches state in cookie 2 // 2. Read user identity from your session, not from the URL 3 4 const { postUserVerifyRedirectUrl } = 5 await scalekit.actions.verifyConnectedAccountUser({ 6 authRequestId: auth_request_id, 7 identifier: userId, // must match what was stored at link creation 8 }); 9 // On success: redirect to postUserVerifyRedirectUrl ``` On success, the connected account is activated. Redirect the user using `post_user_verify_redirect_url`. --- # DOCUMENT BOUNDARY --- # Claude Integration > Integrate Scalekit with Claude for AI-powered authentication workflows Coming soon --- # DOCUMENT BOUNDARY --- # Codex Integration > Use Scalekit with Codex for automated authentication code generation Coming soon --- # DOCUMENT BOUNDARY --- # Use Scalekit docs in your AI coding agent > Use Context7 to give your AI coding agent accurate, up-to-date Scalekit documentation so it can help you integrate faster and with fewer errors. AI coding agents like Claude Code and Cursor work from training data that can be months out of date. When you ask them to help integrate Scalekit, they may reference old APIs, deprecated patterns, or incorrect parameter names — leading to bugs that are hard to trace. [Context7](https://context7.com) provides two ways to access live, version-accurate documentation: * **CLI** — query docs directly from your terminal (recommended for most developers) * **MCP server** — integrates with AI agents for automatic doc injection Both methods pull the same up-to-date content. Choose CLI for direct control, or MCP server for seamless AI agent integration. Scalekit’s full developer documentation is indexed on Context7 at [context7.com/scalekit-inc/developer-docs](https://context7.com/scalekit-inc/developer-docs), covering hundreds of pages and thousands of code snippets across SSO, SCIM, MCP auth, agent auth, and connected accounts. ## Get accurate answers about Scalekit [Section titled “Get accurate answers about Scalekit”](#get-accurate-answers-about-scalekit) Context7 retrieves relevant documentation from the indexed Scalekit docs and delivers it to you or your agent. The AI then answers using accurate, current content rather than training data. Context7 provides three main capabilities: * `library` — resolve library IDs and discover docs * `docs` — fetch specific documentation sections * MCP server tools for AI agent integration 1. #### Set up Context7 [Section titled “Set up Context7”](#set-up-context7) Context7 can be set up via CLI or as an MCP server. Choose your method: * CLI Install the Context7 CLI to query docs directly from your terminal. **One-off installation via npx:** ```sh npx ctx7 --help ``` **Global installation:** ```sh npm install -g ctx7 ctx7 --version ``` Requires Node.js 18 or higher. The CLI provides three main capabilities: * **Fetch docs** — query specific documentation sections * **Manage skills** — generate AI agent skills for auto-invocation * **Configure MCP** — set up MCP server integration * MCP Server Context7 is configured as an MCP server in your coding agent. You can also add it directly from [context7.com](https://context7.com). Choose your tool: * Claude Code Run one of the following commands in your terminal: **Local (stdio):** ```sh claude mcp add --scope user context7 -- npx -y @upstash/context7-mcp ``` **Remote (HTTP):** ```sh claude mcp add --scope user --transport http context7 https://mcp.context7.com/mcp ``` To verify the server was added: ```sh claude mcp list ``` * Cursor 1. Open **Settings > Cursor Settings > MCP** and click **Add New Global MCP Server**. Paste one of the following configs: **Remote server:** ```json { "mcpServers": { "context7": { "url": "https://mcp.context7.com/mcp" } } } ``` **Local server:** ```json { "mcpServers": { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp"] } } } ``` 2. Restart Cursor. * Claude Desktop The easiest way is to install Context7 directly from the Claude Desktop interface: 1. Open Claude Desktop and go to **Customize > Connectors**. 2. Search for **Context7** and click **Install**. Alternatively, configure it manually via **Settings > Developer > Edit Config** and add to `claude_desktop_config.json`: ```json { "mcpServers": { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp"] } } } ``` Restart Claude Desktop after saving. * Windsurf 1. Open **Settings > Developer > Edit Config** and open `windsurf_config.json`. 2. Add the following config and save: ```json { "mcpServers": { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp"] } } } ``` 3. Restart Windsurf. * Claude Code Run one of the following commands in your terminal: **Local (stdio):** ```sh claude mcp add --scope user context7 -- npx -y @upstash/context7-mcp ``` **Remote (HTTP):** ```sh claude mcp add --scope user --transport http context7 https://mcp.context7.com/mcp ``` To verify the server was added: ```sh claude mcp list ``` * Cursor 1. Open **Settings > Cursor Settings > MCP** and click **Add New Global MCP Server**. Paste one of the following configs: **Remote server:** ```json { "mcpServers": { "context7": { "url": "https://mcp.context7.com/mcp" } } } ``` **Local server:** ```json { "mcpServers": { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp"] } } } ``` 2. Restart Cursor. * Claude Desktop The easiest way is to install Context7 directly from the Claude Desktop interface: 1. Open Claude Desktop and go to **Customize > Connectors**. 2. Search for **Context7** and click **Install**. Alternatively, configure it manually via **Settings > Developer > Edit Config** and add to `claude_desktop_config.json`: ```json { "mcpServers": { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp"] } } } ``` Restart Claude Desktop after saving. * Windsurf 1. Open **Settings > Developer > Edit Config** and open `windsurf_config.json`. 2. Add the following config and save: ```json { "mcpServers": { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp"] } } } ``` 3. Restart Windsurf. 2. #### Query Scalekit docs [Section titled “Query Scalekit docs”](#query-scalekit-docs) * Using CLI Querying Scalekit docs via CLI is a two-step process. **Step 1 — Resolve Scalekit library:** ```sh ctx7 library scalekit "How to set up SSO" ctx7 library scalekit "SCIM user provisioning" ctx7 library scalekit "MCP authentication setup" ``` Expected result for library selection: | Field | Description | | ----------------- | ----------------------------------- | | Library ID | `/scalekit-inc/developer-docs` | | Code Snippets | High (hundreds of indexed examples) | | Source Reputation | High | | Benchmark Score | Quality score from 0 to 100 | **Step 2 — Fetch Scalekit docs:** ```sh # SSO queries ctx7 docs /scalekit-inc/developer-docs "How to set up SSO with Scalekit" ctx7 docs /scalekit-inc/developer-docs "Configure SAML for enterprise SSO" # SCIM queries ctx7 docs /scalekit-inc/developer-docs "How to provision users with SCIM" ctx7 docs /scalekit-inc/developer-docs "Set up SCIM for Active Directory" # MCP auth queries ctx7 docs /scalekit-inc/developer-docs "Add MCP auth to my server" ctx7 docs /scalekit-inc/developer-docs "Configure agent authentication" # Connected accounts queries ctx7 docs /scalekit-inc/developer-docs "Configure connected accounts for GitHub OAuth" ctx7 docs /scalekit-inc/developer-docs "Set up Google OAuth integration" # JSON output for scripting ctx7 docs /scalekit-inc/developer-docs "SSO setup" --json # Pipe to other tools ctx7 docs /scalekit-inc/developer-docs "SCIM provisioning" | head -50 ``` * Using MCP Server Once Context7 is running, add **`use context7`** to any prompt where you want current Scalekit documentation injected automatically. **General Scalekit queries:** ```txt How do I set up SSO with Scalekit? use context7 ``` ```txt Show me how to provision users with SCIM using Scalekit. use context7 ``` **Target Scalekit docs directly** using the library path: ```txt use library /scalekit-inc/developer-docs for how to add MCP auth to my server ``` **Combine with version or feature specificity:** ```txt How do I configure connected accounts for GitHub OAuth with Scalekit? use context7 ``` 3. #### Auto-invoke Context7 (optional) [Section titled “Auto-invoke Context7 (optional)”](#auto-invoke-context7-optional) Configure your coding agent to always use Context7 for library and API questions — no need to add “use context7” manually each time. * CLI Use `ctx7 setup --cli` to configure Context7 for AI coding agents. This installs a `docs` skill that guides the agent to use `ctx7 library` and `ctx7 docs` commands for Scalekit documentation. **Setup commands:** ```sh # Interactive setup (prompts for agent) ctx7 setup --cli # Direct setup for specific agents ctx7 setup --cli --claude # Claude Code (~/.claude/skills) ctx7 setup --cli --cursor # Cursor (~/.cursor/skills) ctx7 setup --cli --universal # Universal (~/.config/agents/skills) # Project-specific setup (default is global) ctx7 setup --cli --project # Skip confirmation prompts ctx7 setup --cli --yes ``` **What gets installed — CLI + Skills mode:** | File | Purpose | | ---------------------- | --------------------------------------------------------------------- | | Agent skills directory | `docs` skill — guides the agent to use `ctx7 library` and `ctx7 docs` | When the `docs` skill is installed, your AI agent will automatically use `ctx7` commands to fetch accurate Scalekit documentation when asked about SSO, SCIM, MCP auth, or other Scalekit features. * MCP Server Configure your coding agent to always use Context7 for library and API questions — no need to add “use context7” manually each time. * Claude Code Add the following rule to your project’s `CLAUDE.md` file: ```md Always use Context7 MCP when I need library or API documentation, code generation, or setup and configuration steps. ``` This applies project-wide. For a global rule, add it to `~/.claude/CLAUDE.md`. * Cursor Open **Settings > Cursor Settings > Rules** and add: ```txt Always use Context7 MCP when I need library or API documentation, code generation, or setup and configuration steps. ``` * Claude Code Add the following rule to your project’s `CLAUDE.md` file: ```md Always use Context7 MCP when I need library or API documentation, code generation, or setup and configuration steps. ``` This applies project-wide. For a global rule, add it to `~/.claude/CLAUDE.md`. * Cursor Open **Settings > Cursor Settings > Rules** and add: ```txt Always use Context7 MCP when I need library or API documentation, code generation, or setup and configuration steps. ``` 4. #### Increase rate limits with an API key [Section titled “Increase rate limits with an API key”](#increase-rate-limits-with-an-api-key) The free tier of Context7 has rate limits. For heavier usage or team environments, get a free API key from [context7.com/dashboard](https://context7.com/dashboard) and add it to your configuration. * MCP Server * Claude Code **Local:** ```sh claude mcp add --scope user context7 -- npx -y @upstash/context7-mcp --api-key YOUR_API_KEY ``` **Remote:** ```sh claude mcp add --scope user --header "CONTEXT7_API_KEY: YOUR_API_KEY" --transport http context7 https://mcp.context7.com/mcp ``` * Cursor **Remote server with API key:** ```json { "mcpServers": { "context7": { "url": "https://mcp.context7.com/mcp", "headers": { "CONTEXT7_API_KEY": "YOUR_API_KEY" } } } } ``` **Local server with API key:** ```json { "mcpServers": { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp", "--api-key", "YOUR_API_KEY"] } } } ``` * Claude Desktop / Windsurf ```json { "mcpServers": { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp", "--api-key", "YOUR_API_KEY"] } } } ``` * CLI **Local:** ```sh claude mcp add --scope user context7 -- npx -y @upstash/context7-mcp --api-key YOUR_API_KEY ``` **Remote:** ```sh claude mcp add --scope user --header "CONTEXT7_API_KEY: YOUR_API_KEY" --transport http context7 https://mcp.context7.com/mcp ``` * Claude Code **Remote server with API key:** ```json { "mcpServers": { "context7": { "url": "https://mcp.context7.com/mcp", "headers": { "CONTEXT7_API_KEY": "YOUR_API_KEY" } } } } ``` **Local server with API key:** ```json { "mcpServers": { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp", "--api-key", "YOUR_API_KEY"] } } } ``` * Cursor ```json { "mcpServers": { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp", "--api-key", "YOUR_API_KEY"] } } } ``` * Claude Desktop / Windsurf Set an API key via environment variable for higher rate limits: ```sh # Set API key for current session export CONTEXT7_API_KEY=your_key # Add to ~/.bashrc or ~/.zshrc for permanent use echo 'export CONTEXT7_API_KEY=your_key' >> ~/.bashrc ``` API keys start with `ctx7sk`. If authentication fails with a 401 error, verify the key format matches your method (HTTP header for MCP, environment variable for CLI). --- # DOCUMENT BOUNDARY --- # Cursor Integration > Use Scalekit with Cursor via the local installer while the marketplace listing is under review Use Scalekit with Cursor by running the local installer, enabling the auth plugin you need, and then prompting Cursor to generate the implementation in your existing codebase. 1. ## Install the Scalekit Auth Stack locally Terminal ```bash curl -fsSL https://raw.githubusercontent.com/scalekit-inc/cursor-authstack/main/install.sh | bash ``` This installer downloads the latest Scalekit Cursor plugin bundle and installs each auth plugin into `~/.cursor/plugins/local/`. 2. ## Reload Cursor and enable the plugin Restart Cursor, or run **Developer: Reload Window**, then open **Settings > Cursor Settings > Plugins**. Select the authentication plugin you need, such as **Full Stack Auth**, **Modular SSO**, or **MCP Auth**, and enable it. 3. ## Generate the implementation Open Cursor’s chat panel with **Cmd+L** (macOS) or **Ctrl+L** (Windows/Linux) and paste in an implementation prompt. Use the same prompt from the corresponding Claude Code tab — the Scalekit plugins and their authentication skills work identically in Cursor. Review generated code Always review AI-generated authentication code before deployment. Verify that environment variables, token validation logic, and error handling match your application’s security requirements. 4. ## Verify the implementation After Cursor finishes generating code, confirm all authentication components are in place: * The Scalekit plugin appears in **Settings > Cursor Settings > Plugins** * Scalekit client initialized with your API credentials (set up a `.env` file with your Scalekit environment variables) * Authorization URL generation and callback handler * Session or token integration matching your application’s existing patterns Once the Scalekit Auth Stack is live on [cursor.com/marketplace](https://cursor.com/marketplace), you’ll be able to skip the local installer and install it directly inside Cursor. --- # DOCUMENT BOUNDARY --- # Scalekit MCP Server > Learn how to use the Scalekit MCP Server to manage your users, organizations, and applications. Scalekit Model Context Protocol (MCP) server provides comprehensive tools for managing environments, organizations, users, connections, and workspace operations. Built for developers who want to connect their AI tools to Scalekit context and capabilities based on simple natural language queries. This MCP server enables AI assistants to interact with Scalekit’s identity and access management platform through a standardized set of tools. It provides secure, OAuth-protected access to manage environments, organizations, users, authentication connections, and more. * Environment management and configuration * Organization and user management * Workspace member administration * OIDC connection setup and management * MCP server registration and configuration * Role and scope management * Admin portal link generation ## Configuration [Section titled “Configuration”](#configuration) Connect the Scalekit MCP server to your AI coding tool. Find your tool below and follow the steps — your client will prompt you to sign in via OAuth on first use. ### Claude Code [Section titled “Claude Code”](#claude-code) Run this command in your terminal: ```bash 1 claude mcp add --transport http scalekit https://mcp.scalekit.com/ ``` ### Claude Desktop [Section titled “Claude Desktop”](#claude-desktop) 1. Open Claude Desktop 2. Go to **Settings → Connectors** 3. Click **Add custom connector** 4. Enter `Scalekit` as the name and `https://mcp.scalekit.com` as the URL 5. Click **Connect** to authenticate ### VS Code [Section titled “VS Code”](#vs-code) Edit `.vscode/mcp.json` in your project (requires VS Code 1.101 or later): ```json 1 { 2 "servers": { 3 "scalekit": { 4 "type": "http", 5 "url": "https://mcp.scalekit.com/" 6 } 7 } 8 } ``` ### Cursor [Section titled “Cursor”](#cursor) Edit `~/.cursor/mcp.json`, or open **Cursor Settings → MCP → Add New Global MCP Server** and paste the config: ```json 1 { 2 "mcpServers": { 3 "scalekit": { 4 "url": "https://mcp.scalekit.com/" 5 } 6 } 7 } ``` ### Windsurf [Section titled “Windsurf”](#windsurf) Edit `~/.codeium/windsurf/mcp_config.json`: ```json 1 { 2 "mcpServers": { 3 "scalekit": { 4 "serverUrl": "https://mcp.scalekit.com/" 5 } 6 } 7 } ``` ### Gemini CLI [Section titled “Gemini CLI”](#gemini-cli) Edit `~/.gemini/settings.json`: ```json 1 { 2 "mcpServers": { 3 "scalekit": { 4 "httpUrl": "https://mcp.scalekit.com/" 5 } 6 } 7 } ``` ### Codex [Section titled “Codex”](#codex) Run this command in your terminal: ```bash 1 codex mcp add scalekit --url https://mcp.scalekit.com/ ``` ### OpenCode [Section titled “OpenCode”](#opencode) Edit `opencode.json` in your project root: ```json 1 { 2 "mcp": { 3 "scalekit": { 4 "type": "remote", 5 "url": "https://mcp.scalekit.com/", 6 "enabled": true 7 } 8 } 9 } ``` ### Roo Code [Section titled “Roo Code”](#roo-code) Add to your MCP configuration: ```json 1 { 2 "mcpServers": { 3 "scalekit": { 4 "type": "streamable-http", 5 "url": "https://mcp.scalekit.com/" 6 } 7 } 8 } ``` ### Zed [Section titled “Zed”](#zed) Add to your Zed `settings.json`: ```json 1 { 2 "context_servers": { 3 "scalekit": { 4 "url": "https://mcp.scalekit.com/" 5 } 6 } 7 } ``` ### Kiro [Section titled “Kiro”](#kiro) Edit `~/.kiro/settings/mcp.json`: ```json 1 { 2 "mcpServers": { 3 "scalekit": { 4 "url": "https://mcp.scalekit.com/" 5 } 6 } 7 } ``` ### Warp [Section titled “Warp”](#warp) Go to **Settings → MCP Servers → Add MCP Server** and enter `https://mcp.scalekit.com/`, or add to your Warp MCP config: ```json 1 { 2 "scalekit": { 3 "serverUrl": "https://mcp.scalekit.com/" 4 } 5 } ``` ### v0 by Vercel [Section titled “v0 by Vercel”](#v0-by-vercel) Go to **Prompt Tools → Add MCP** and enter `https://mcp.scalekit.com/`. ## GitHub [Section titled “GitHub”](#github) The source code for the Scalekit MCP server is available on [GitHub](https://github.com/scalekit-inc/mcp), including a full list of available tools and their descriptions. * Open an issue if you find a bug or have a question. * Submit a PR or open an issue to suggest new tools. --- # DOCUMENT BOUNDARY --- # VS Code Extension > Enhance your development workflow with the Scalekit VS Code extension Coming soon --- # DOCUMENT BOUNDARY --- # Agent connectors > Connect AI applications to tools and data from Slack, Google Workspace, Salesforce, and more. Agent connectors enable AI-powered applications to connect to tools and data from popular platforms such as Slack, Google Workspace, Salesforce, Notion, and more. Each connector provides OAuth or API key authentication and exposes tools your agents can use. ⌕ Search connectors… All categories (all) All auth types (all) ## AI [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/apify.svg)](/agentkit/connectors/apifymcp/) [Apify MCP connector](/agentkit/connectors/apifymcp/) [Connect to Apify MCP to run web scraping, browser automation, and data extraction Actors directly from your AI workflows.](/agentkit/connectors/apifymcp/) [Bearer Token](/agentkit/connectors/apifymcp/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/attention.svg)](/agentkit/connectors/attention/) [Attention connector](/agentkit/connectors/attention/) [Connect to Attention for AI insights, conversations, teams, and workflows](/agentkit/connectors/attention/) [API Key](/agentkit/connectors/attention/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/chorus.svg)](/agentkit/connectors/chorus/) [Chorus connector](/agentkit/connectors/chorus/) [Connect to Chorus.ai to sync calls, transcripts, conversation intelligence, and analytics.](/agentkit/connectors/chorus/) [Basic Auth](/agentkit/connectors/chorus/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/clari.svg)](/agentkit/connectors/clari_copilot/) [Clari Copilot connector](/agentkit/connectors/clari_copilot/) [Connect to Clari Copilot for sales call transcripts, analytics, call data, and insights.](/agentkit/connectors/clari_copilot/) [API Key](/agentkit/connectors/clari_copilot/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/exa.svg)](/agentkit/connectors/exa/) [Exa connector](/agentkit/connectors/exa/) [Connect to Exa to perform AI-powered semantic web search, crawl websites for structured content, get natural language answers from the web, run in-depth...](/agentkit/connectors/exa/) [API Key](/agentkit/connectors/exa/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/fathom.svg)](/agentkit/connectors/fathom/) [Fathom connector](/agentkit/connectors/fathom/) [Connect to Fathom AI meeting assistant. Record, transcribe, and summarize meetings with AI-powered insights](/agentkit/connectors/fathom/) [API Key](/agentkit/connectors/fathom/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/gong.svg)](/agentkit/connectors/gong/) [Gong connector](/agentkit/connectors/gong/) [Connect with Gong to sync calls, transcripts, insights, coaching and CRM activity](/agentkit/connectors/gong/) [OAuth 2.0](/agentkit/connectors/gong/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/granola.svg)](/agentkit/connectors/granola/) [Granola connector](/agentkit/connectors/granola/) [Connect to Granola to access AI-generated meeting notes, summaries, transcripts, and attendee data from your workspace. Granola automatically records and...](/agentkit/connectors/granola/) [Bearer Token](/agentkit/connectors/granola/) [![](https://cdn.scalekit.cloud/sk-connect/assets/provider-icons/granola.svg)](/agentkit/connectors/granolamcp/) [Granola MCP connector](/agentkit/connectors/granolamcp/) [Connect to Granola MCP using OAuth 2.1 with MCP discovery and dynamic client registration.](/agentkit/connectors/granolamcp/) [OAuth 2.0](/agentkit/connectors/granolamcp/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/jiminny.svg)](/agentkit/connectors/jiminny/) [Jiminny connector](/agentkit/connectors/jiminny/) [Connect with Jiminny to access call recordings, transcripts, coaching insights, and conversation intelligence data.](/agentkit/connectors/jiminny/) [Bearer Token](/agentkit/connectors/jiminny/) [![](https://cdn.scalekit.cloud/sk-connect/assets/provider-icons/parallel-ai.svg)](/agentkit/connectors/parallelaitaskmcp/) [Parallel AI Task MCP connector](/agentkit/connectors/parallelaitaskmcp/) [Connect to Parallel AI Task MCP to run deep research tasks and task groups directly from your AI workflows.](/agentkit/connectors/parallelaitaskmcp/) [Bearer Token](/agentkit/connectors/parallelaitaskmcp/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/phantombuster.svg)](/agentkit/connectors/phantombuster/) [PhantomBuster connector](/agentkit/connectors/phantombuster/) [Connect to PhantomBuster to automate web scraping and data extraction workflows. Launch, monitor, and manage automation agents that extract data from...](/agentkit/connectors/phantombuster/) [API Key](/agentkit/connectors/phantombuster/) ## Analytics [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/airtable.svg)](/agentkit/connectors/airtable/) [Airtable connector](/agentkit/connectors/airtable/) [Connect to Airtable. Manage databases, tables, records, and collaborate on structured data](/agentkit/connectors/airtable/) [OAuth 2.0](/agentkit/connectors/airtable/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/bigquery.svg)](/agentkit/connectors/bigqueryserviceaccount/) [BigQuery (Service Account) connector](/agentkit/connectors/bigqueryserviceaccount/) [Connect to Google BigQuery using a GCP service account for server-to-server authentication without user login.](/agentkit/connectors/bigqueryserviceaccount/) [Service Account](/agentkit/connectors/bigqueryserviceaccount/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/brave.svg)](/agentkit/connectors/brave/) [Brave Search connector](/agentkit/connectors/brave/) [Connect to Brave Search to perform web, image, video, and news searches with privacy-focused results, plus AI-powered suggestions and spellcheck.](/agentkit/connectors/brave/) [API Key](/agentkit/connectors/brave/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/databricks-1.svg)](/agentkit/connectors/databricksworkspace/) [Databricks Workspace connector](/agentkit/connectors/databricksworkspace/) [Connect to Databricks Workspace APIs using a Service Principal with OAuth 2.0 client credentials to manage clusters, jobs, notebooks, SQL, and more.](/agentkit/connectors/databricksworkspace/) [Service Principal (OAuth 2.0)](/agentkit/connectors/databricksworkspace/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/diarize.svg)](/agentkit/connectors/diarize/) [Diarize connector](/agentkit/connectors/diarize/) [Connect to Diarize to transcribe and diarize audio and video content from YouTube, X, Instagram, and TikTok. Submit transcription jobs and retrieve...](/agentkit/connectors/diarize/) [Bearer Token](/agentkit/connectors/diarize/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/exa.svg)](/agentkit/connectors/exa/) [Exa connector](/agentkit/connectors/exa/) [Connect to Exa to perform AI-powered semantic web search, crawl websites for structured content, get natural language answers from the web, run in-depth...](/agentkit/connectors/exa/) [API Key](/agentkit/connectors/exa/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/bigquery.svg)](/agentkit/connectors/bigquery/) [Google BigQuery connector](/agentkit/connectors/bigquery/) [BigQuery is Google Cloud’s fully-managed enterprise data warehouse for analytics at scale.](/agentkit/connectors/bigquery/) [OAuth 2.0](/agentkit/connectors/bigquery/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/google_sheets.svg)](/agentkit/connectors/googlesheets/) [Google Sheets connector](/agentkit/connectors/googlesheets/) [Connect to Google Sheets. Create, edit, and analyze spreadsheets with powerful data management capabilities](/agentkit/connectors/googlesheets/) [OAuth 2.0](/agentkit/connectors/googlesheets/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/excel.svg)](/agentkit/connectors/microsoftexcel/) [Microsoft Excel connector](/agentkit/connectors/microsoftexcel/) [Connect to Microsoft Excel. Access, read, and modify spreadsheets stored in OneDrive or SharePoint through Microsoft Graph API.](/agentkit/connectors/microsoftexcel/) [OAuth 2.0](/agentkit/connectors/microsoftexcel/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/snowflake.svg)](/agentkit/connectors/snowflake/) [Snowflake connector](/agentkit/connectors/snowflake/) [Connect to Snowflake to manage and analyze your data warehouse workloads](/agentkit/connectors/snowflake/) [OAuth 2.0](/agentkit/connectors/snowflake/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/snowflake.svg)](/agentkit/connectors/snowflakekeyauth/) [Snowflake Key Pair Auth connector](/agentkit/connectors/snowflakekeyauth/) [Connect to Snowflake via Public Private Key Pair to manage and analyze your data warehouse workloads](/agentkit/connectors/snowflakekeyauth/) [Bearer Token](/agentkit/connectors/snowflakekeyauth/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/supadata.svg)](/agentkit/connectors/supadata/) [Supadata connector](/agentkit/connectors/supadata/) [Connect with Supadata to extract transcripts, metadata, and structured content from YouTube, social media, and the web using AI.](/agentkit/connectors/supadata/) [API Key](/agentkit/connectors/supadata/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/tableau.svg)](/agentkit/connectors/tableau/) [Tableau connector](/agentkit/connectors/tableau/) [Connect to Tableau Cloud or Tableau Server to browse workbooks, views, and data sources, export visualizations, and query underlying data.](/agentkit/connectors/tableau/) [API Key](/agentkit/connectors/tableau/) ## Automation [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/apify.svg)](/agentkit/connectors/apifymcp/) [Apify MCP connector](/agentkit/connectors/apifymcp/) [Connect to Apify MCP to run web scraping, browser automation, and data extraction Actors directly from your AI workflows.](/agentkit/connectors/apifymcp/) [Bearer Token](/agentkit/connectors/apifymcp/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/attention.svg)](/agentkit/connectors/attention/) [Attention connector](/agentkit/connectors/attention/) [Connect to Attention for AI insights, conversations, teams, and workflows](/agentkit/connectors/attention/) [API Key](/agentkit/connectors/attention/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/chorus.svg)](/agentkit/connectors/chorus/) [Chorus connector](/agentkit/connectors/chorus/) [Connect to Chorus.ai to sync calls, transcripts, conversation intelligence, and analytics.](/agentkit/connectors/chorus/) [Basic Auth](/agentkit/connectors/chorus/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/clari.svg)](/agentkit/connectors/clari_copilot/) [Clari Copilot connector](/agentkit/connectors/clari_copilot/) [Connect to Clari Copilot for sales call transcripts, analytics, call data, and insights.](/agentkit/connectors/clari_copilot/) [API Key](/agentkit/connectors/clari_copilot/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/databricks-1.svg)](/agentkit/connectors/databricksworkspace/) [Databricks Workspace connector](/agentkit/connectors/databricksworkspace/) [Connect to Databricks Workspace APIs using a Service Principal with OAuth 2.0 client credentials to manage clusters, jobs, notebooks, SQL, and more.](/agentkit/connectors/databricksworkspace/) [Service Principal (OAuth 2.0)](/agentkit/connectors/databricksworkspace/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/exa.svg)](/agentkit/connectors/exa/) [Exa connector](/agentkit/connectors/exa/) [Connect to Exa to perform AI-powered semantic web search, crawl websites for structured content, get natural language answers from the web, run in-depth...](/agentkit/connectors/exa/) [API Key](/agentkit/connectors/exa/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/fathom.svg)](/agentkit/connectors/fathom/) [Fathom connector](/agentkit/connectors/fathom/) [Connect to Fathom AI meeting assistant. Record, transcribe, and summarize meetings with AI-powered insights](/agentkit/connectors/fathom/) [API Key](/agentkit/connectors/fathom/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/gong.svg)](/agentkit/connectors/gong/) [Gong connector](/agentkit/connectors/gong/) [Connect with Gong to sync calls, transcripts, insights, coaching and CRM activity](/agentkit/connectors/gong/) [OAuth 2.0](/agentkit/connectors/gong/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/granola.svg)](/agentkit/connectors/granola/) [Granola connector](/agentkit/connectors/granola/) [Connect to Granola to access AI-generated meeting notes, summaries, transcripts, and attendee data from your workspace. Granola automatically records and...](/agentkit/connectors/granola/) [Bearer Token](/agentkit/connectors/granola/) [![](https://cdn.scalekit.cloud/sk-connect/assets/provider-icons/granola.svg)](/agentkit/connectors/granolamcp/) [Granola MCP connector](/agentkit/connectors/granolamcp/) [Connect to Granola MCP using OAuth 2.1 with MCP discovery and dynamic client registration.](/agentkit/connectors/granolamcp/) [OAuth 2.0](/agentkit/connectors/granolamcp/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/jiminny.svg)](/agentkit/connectors/jiminny/) [Jiminny connector](/agentkit/connectors/jiminny/) [Connect with Jiminny to access call recordings, transcripts, coaching insights, and conversation intelligence data.](/agentkit/connectors/jiminny/) [Bearer Token](/agentkit/connectors/jiminny/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/phantombuster.svg)](/agentkit/connectors/phantombuster/) [PhantomBuster connector](/agentkit/connectors/phantombuster/) [Connect to PhantomBuster to automate web scraping and data extraction workflows. Launch, monitor, and manage automation agents that extract data from...](/agentkit/connectors/phantombuster/) [API Key](/agentkit/connectors/phantombuster/) ## Business Intelligence [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/tableau.svg)](/agentkit/connectors/tableau/) [Tableau connector](/agentkit/connectors/tableau/) [Connect to Tableau Cloud or Tableau Server to browse workbooks, views, and data sources, export visualizations, and query underlying data.](/agentkit/connectors/tableau/) [API Key](/agentkit/connectors/tableau/) ## Calendar [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/calendly.svg)](/agentkit/connectors/calendly/) [Calendly connector](/agentkit/connectors/calendly/) [Connect to Calendly. Access user profile, events, and scheduling workflows.](/agentkit/connectors/calendly/) [OAuth 2.0](/agentkit/connectors/calendly/) ## CI/CD [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/bitbucket.svg)](/agentkit/connectors/bitbucket/) [Bitbucket connector](/agentkit/connectors/bitbucket/) [Connect to Bitbucket. Manage repositories, pipelines, pull requests, and code collaboration.](/agentkit/connectors/bitbucket/) [OAuth 2.0](/agentkit/connectors/bitbucket/) ## Collaboration [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/bitbucket.svg)](/agentkit/connectors/bitbucket/) [Bitbucket connector](/agentkit/connectors/bitbucket/) [Connect to Bitbucket. Manage repositories, pipelines, pull requests, and code collaboration.](/agentkit/connectors/bitbucket/) [OAuth 2.0](/agentkit/connectors/bitbucket/) ## Communication [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/close.svg)](/agentkit/connectors/close/) [Close connector](/agentkit/connectors/close/) [Connect to Close CRM. Manage leads, contacts, opportunities, tasks, activities, and sales workflows](/agentkit/connectors/close/) [OAuth 2.0](/agentkit/connectors/close/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/discord.svg)](/agentkit/connectors/discord/) [Discord connector](/agentkit/connectors/discord/) [Connect to Discord. Read user profile, guilds, roles, manage bots, and perform interactions.](/agentkit/connectors/discord/) [OAuth 2.0](/agentkit/connectors/discord/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/fathom.svg)](/agentkit/connectors/fathom/) [Fathom connector](/agentkit/connectors/fathom/) [Connect to Fathom AI meeting assistant. Record, transcribe, and summarize meetings with AI-powered insights](/agentkit/connectors/fathom/) [API Key](/agentkit/connectors/fathom/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/gmail.svg)](/agentkit/connectors/gmail/) [Gmail connector](/agentkit/connectors/gmail/) [Gmail is Google's cloud based email service that allows you to access your messages from any computer or device with just a web browser.](/agentkit/connectors/gmail/) [OAuth 2.0](/agentkit/connectors/gmail/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/google_calendar.svg)](/agentkit/connectors/googlecalendar/) [Google Calendar connector](/agentkit/connectors/googlecalendar/) [Google Calendar is Google's cloud-based calendar service that allows you to manage your events, appointments, and schedules from any computer or device...](/agentkit/connectors/googlecalendar/) [OAuth 2.0](/agentkit/connectors/googlecalendar/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/google_meet.svg)](/agentkit/connectors/googlemeet/) [Google Meet connector](/agentkit/connectors/googlemeet/) [Connect to Google Meet. Create and manage video meetings with powerful collaboration features](/agentkit/connectors/googlemeet/) [OAuth 2.0](/agentkit/connectors/googlemeet/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/granola.svg)](/agentkit/connectors/granola/) [Granola connector](/agentkit/connectors/granola/) [Connect to Granola to access AI-generated meeting notes, summaries, transcripts, and attendee data from your workspace. Granola automatically records and...](/agentkit/connectors/granola/) [Bearer Token](/agentkit/connectors/granola/) [![](https://cdn.scalekit.cloud/sk-connect/assets/provider-icons/granola.svg)](/agentkit/connectors/granolamcp/) [Granola MCP connector](/agentkit/connectors/granolamcp/) [Connect to Granola MCP using OAuth 2.1 with MCP discovery and dynamic client registration.](/agentkit/connectors/granolamcp/) [OAuth 2.0](/agentkit/connectors/granolamcp/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/intercom.svg)](/agentkit/connectors/intercom/) [Intercom connector](/agentkit/connectors/intercom/) [Connect to Intercom. Send messages, manage conversations, and interact with users and contacts.](/agentkit/connectors/intercom/) [OAuth 2.0](/agentkit/connectors/intercom/) [![](https://cdn.scalekit.cloud/sk-connect/assets/provider-icons/outlook.svg)](/agentkit/connectors/outlook/) [Outlook connector](/agentkit/connectors/outlook/) [Connect to Microsoft Outlook. Manage emails, calendar events, contacts, and tasks](/agentkit/connectors/outlook/) [OAuth 2.0](/agentkit/connectors/outlook/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/slack.svg)](/agentkit/connectors/slack/) [Slack connector](/agentkit/connectors/slack/) [Connect to Slack workspace. Send Messages as Bots or on behalf of users](/agentkit/connectors/slack/) [OAuth 2.0](/agentkit/connectors/slack/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/microsoft-teams.svg)](/agentkit/connectors/microsoftteams/) [Teams connector](/agentkit/connectors/microsoftteams/) [Connect to Microsoft Teams. Manage messages, channels, meetings, and team collaboration](/agentkit/connectors/microsoftteams/) [OAuth 2.0](/agentkit/connectors/microsoftteams/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/X.svg)](/agentkit/connectors/twitter/) [Twitter / X connector](/agentkit/connectors/twitter/) [Connect to Twitter. Read and write Tweets, read users, manage follows, bookmarks, etc.](/agentkit/connectors/twitter/) [Bearer Token](/agentkit/connectors/twitter/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/vimeo.svg)](/agentkit/connectors/vimeo/) [Vimeo connector](/agentkit/connectors/vimeo/) [Connect to Vimeo API v3.4. Upload and manage videos, organize content into showcases and folders, manage channels, handle comments, likes, and webhooks.](/agentkit/connectors/vimeo/) [OAuth 2.0](/agentkit/connectors/vimeo/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/youtube.svg)](/agentkit/connectors/youtube/) [YouTube connector](/agentkit/connectors/youtube/) [Connect to YouTube to access channel details, analytics, and upload or manage videos via OAuth 2.0](/agentkit/connectors/youtube/) [OAuth 2.0](/agentkit/connectors/youtube/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/zoom.svg)](/agentkit/connectors/zoom/) [Zoom connector](/agentkit/connectors/zoom/) [Connect to Zoom. Schedule meetings, manage recordings, and handle video conferencing workflows](/agentkit/connectors/zoom/) [OAuth 2.0](/agentkit/connectors/zoom/) ## CRM [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/affinity.svg)](/agentkit/connectors/affinity/) [Affinity connector](/agentkit/connectors/affinity/) [Connect to Affinity relationship intelligence CRM to manage deal flow, relationships, pipeline opportunities, and network connections for private capital...](/agentkit/connectors/affinity/) [Bearer Token](/agentkit/connectors/affinity/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/apollo.svg)](/agentkit/connectors/apollo/) [Apollo connector](/agentkit/connectors/apollo/) [Connect to Apollo.io to search and enrich B2B contacts and accounts, manage CRM contacts, and automate outreach sequences.](/agentkit/connectors/apollo/) [OAuth 2.0](/agentkit/connectors/apollo/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/attention.svg)](/agentkit/connectors/attention/) [Attention connector](/agentkit/connectors/attention/) [Connect to Attention for AI insights, conversations, teams, and workflows](/agentkit/connectors/attention/) [API Key](/agentkit/connectors/attention/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/attio.svg)](/agentkit/connectors/attio/) [Attio connector](/agentkit/connectors/attio/) [Connect to Attio CRM to manage contacts, companies, deals, notes, tasks, and lists with a modern relationship management platform.](/agentkit/connectors/attio/) [OAuth 2.0](/agentkit/connectors/attio/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/chorus.svg)](/agentkit/connectors/chorus/) [Chorus connector](/agentkit/connectors/chorus/) [Connect to Chorus.ai to sync calls, transcripts, conversation intelligence, and analytics.](/agentkit/connectors/chorus/) [Basic Auth](/agentkit/connectors/chorus/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/clari.svg)](/agentkit/connectors/clari_copilot/) [Clari Copilot connector](/agentkit/connectors/clari_copilot/) [Connect to Clari Copilot for sales call transcripts, analytics, call data, and insights.](/agentkit/connectors/clari_copilot/) [API Key](/agentkit/connectors/clari_copilot/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/close.svg)](/agentkit/connectors/close/) [Close connector](/agentkit/connectors/close/) [Connect to Close CRM. Manage leads, contacts, opportunities, tasks, activities, and sales workflows](/agentkit/connectors/close/) [OAuth 2.0](/agentkit/connectors/close/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/dynamo.svg)](/agentkit/connectors/dynamo/) [Dynamo Software connector](/agentkit/connectors/dynamo/) [Connect to Dynamo Software API to access investment management, CRM, and reporting data.](/agentkit/connectors/dynamo/) [Bearer Token](/agentkit/connectors/dynamo/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/evertrace.png)](/agentkit/connectors/evertrace/) [Evertrace AI connector](/agentkit/connectors/evertrace/) [Connect to evertrace.ai to search and manage talent signals, saved searches, and lists. Access rich professional profiles with scoring, experiences, and...](/agentkit/connectors/evertrace/) [Bearer Token](/agentkit/connectors/evertrace/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/gong.svg)](/agentkit/connectors/gong/) [Gong connector](/agentkit/connectors/gong/) [Connect with Gong to sync calls, transcripts, insights, coaching and CRM activity](/agentkit/connectors/gong/) [OAuth 2.0](/agentkit/connectors/gong/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/google_ads.png)](/agentkit/connectors/google_ads/) [Google Ads connector](/agentkit/connectors/google_ads/) [Connect to Google Ads to manage advertising campaigns, analyze performance metrics, and optimize ad spending across Google's advertising platform](/agentkit/connectors/google_ads/) [OAuth 2.0](/agentkit/connectors/google_ads/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/harvestapi.svg)](/agentkit/connectors/harvestapi/) [HarvestAPI connector](/agentkit/connectors/harvestapi/) [Connect to HarvestAPI to scrape LinkedIn profiles, companies, and job listings, and search for people and jobs using LinkedIn data. Enables AI agents to...](/agentkit/connectors/harvestapi/) [API Key](/agentkit/connectors/harvestapi/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/heyreach.svg)](/agentkit/connectors/heyreach/) [HeyReach connector](/agentkit/connectors/heyreach/) [Connect to HeyReach to manage LinkedIn outreach campaigns, lead lists, and conversations. List campaigns, retrieve leads, monitor campaign progress, and...](/agentkit/connectors/heyreach/) [API Key](/agentkit/connectors/heyreach/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/hub_spot.svg)](/agentkit/connectors/hubspot/) [HubSpot connector](/agentkit/connectors/hubspot/) [Connect to HubSpot CRM. Manage contacts, deals, companies, and marketing automation](/agentkit/connectors/hubspot/) [OAuth 2.0](/agentkit/connectors/hubspot/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/jiminny.svg)](/agentkit/connectors/jiminny/) [Jiminny connector](/agentkit/connectors/jiminny/) [Connect with Jiminny to access call recordings, transcripts, coaching insights, and conversation intelligence data.](/agentkit/connectors/jiminny/) [Bearer Token](/agentkit/connectors/jiminny/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/linkedin.svg)](/agentkit/connectors/linkedin/) [LinkedIn connector](/agentkit/connectors/linkedin/) [Connect to LinkedIn to manage user authentication, profile data, email, and professional identity via OAuth 2.0](/agentkit/connectors/linkedin/) [OAuth 2.0](/agentkit/connectors/linkedin/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/outreach.png)](/agentkit/connectors/outreach/) [Outreach connector](/agentkit/connectors/outreach/) [Connect with Outreach to manage prospects, accounts, sequences, emails, calls, and sales engagement workflows.](/agentkit/connectors/outreach/) [OAuth 2.0](/agentkit/connectors/outreach/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/pipedrive.svg)](/agentkit/connectors/pipedrive/) [Pipedrive connector](/agentkit/connectors/pipedrive/) [Connect to Pipedrive CRM. Manage deals, contacts, organizations, activities, leads, and notes to streamline your sales pipeline.](/agentkit/connectors/pipedrive/) [OAuth 2.0](/agentkit/connectors/pipedrive/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/sales_force.svg)](/agentkit/connectors/salesforce/) [Salesforce connector](/agentkit/connectors/salesforce/) [Connect to Salesforce CRM. Manage leads, opportunities, accounts, and customer relationships](/agentkit/connectors/salesforce/) [OAuth 2.0](/agentkit/connectors/salesforce/) ## Customer Support [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/freshdesk.png)](/agentkit/connectors/freshdesk/) [Freshdesk connector](/agentkit/connectors/freshdesk/) [Connect to Freshdesk. Manage tickets, contacts, companies, and customer support workflows](/agentkit/connectors/freshdesk/) [Basic Auth](/agentkit/connectors/freshdesk/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/intercom.svg)](/agentkit/connectors/intercom/) [Intercom connector](/agentkit/connectors/intercom/) [Connect to Intercom. Send messages, manage conversations, and interact with users and contacts.](/agentkit/connectors/intercom/) [OAuth 2.0](/agentkit/connectors/intercom/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/servicenow.svg)](/agentkit/connectors/servicenow/) [ServiceNow connector](/agentkit/connectors/servicenow/) [Connect to ServiceNow. Manage incidents, service requests, CMDB, and IT service management workflows](/agentkit/connectors/servicenow/) [OAuth 2.0](/agentkit/connectors/servicenow/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/zendesk.svg)](/agentkit/connectors/zendesk/) [Zendesk connector](/agentkit/connectors/zendesk/) [Connect to Zendesk. Manage customer support tickets, users, organizations, and help desk operations](/agentkit/connectors/zendesk/) [API KEY](/agentkit/connectors/zendesk/) ## Data [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/airtable.svg)](/agentkit/connectors/airtable/) [Airtable connector](/agentkit/connectors/airtable/) [Connect to Airtable. Manage databases, tables, records, and collaborate on structured data](/agentkit/connectors/airtable/) [OAuth 2.0](/agentkit/connectors/airtable/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/bigquery.svg)](/agentkit/connectors/bigqueryserviceaccount/) [BigQuery (Service Account) connector](/agentkit/connectors/bigqueryserviceaccount/) [Connect to Google BigQuery using a GCP service account for server-to-server authentication without user login.](/agentkit/connectors/bigqueryserviceaccount/) [Service Account](/agentkit/connectors/bigqueryserviceaccount/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/brave.svg)](/agentkit/connectors/brave/) [Brave Search connector](/agentkit/connectors/brave/) [Connect to Brave Search to perform web, image, video, and news searches with privacy-focused results, plus AI-powered suggestions and spellcheck.](/agentkit/connectors/brave/) [API Key](/agentkit/connectors/brave/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/databricks-1.svg)](/agentkit/connectors/databricksworkspace/) [Databricks Workspace connector](/agentkit/connectors/databricksworkspace/) [Connect to Databricks Workspace APIs using a Service Principal with OAuth 2.0 client credentials to manage clusters, jobs, notebooks, SQL, and more.](/agentkit/connectors/databricksworkspace/) [Service Principal (OAuth 2.0)](/agentkit/connectors/databricksworkspace/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/dynamo.svg)](/agentkit/connectors/dynamo/) [Dynamo Software connector](/agentkit/connectors/dynamo/) [Connect to Dynamo Software API to access investment management, CRM, and reporting data.](/agentkit/connectors/dynamo/) [Bearer Token](/agentkit/connectors/dynamo/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/exa.svg)](/agentkit/connectors/exa/) [Exa connector](/agentkit/connectors/exa/) [Connect to Exa to perform AI-powered semantic web search, crawl websites for structured content, get natural language answers from the web, run in-depth...](/agentkit/connectors/exa/) [API Key](/agentkit/connectors/exa/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/bigquery.svg)](/agentkit/connectors/bigquery/) [Google BigQuery connector](/agentkit/connectors/bigquery/) [BigQuery is Google Cloud’s fully-managed enterprise data warehouse for analytics at scale.](/agentkit/connectors/bigquery/) [OAuth 2.0](/agentkit/connectors/bigquery/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/google_sheets.svg)](/agentkit/connectors/googlesheets/) [Google Sheets connector](/agentkit/connectors/googlesheets/) [Connect to Google Sheets. Create, edit, and analyze spreadsheets with powerful data management capabilities](/agentkit/connectors/googlesheets/) [OAuth 2.0](/agentkit/connectors/googlesheets/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/excel.svg)](/agentkit/connectors/microsoftexcel/) [Microsoft Excel connector](/agentkit/connectors/microsoftexcel/) [Connect to Microsoft Excel. Access, read, and modify spreadsheets stored in OneDrive or SharePoint through Microsoft Graph API.](/agentkit/connectors/microsoftexcel/) [OAuth 2.0](/agentkit/connectors/microsoftexcel/) [![](https://cdn.scalekit.cloud/sk-connect/assets/provider-icons/parallel-ai.svg)](/agentkit/connectors/parallelaitaskmcp/) [Parallel AI Task MCP connector](/agentkit/connectors/parallelaitaskmcp/) [Connect to Parallel AI Task MCP to run deep research tasks and task groups directly from your AI workflows.](/agentkit/connectors/parallelaitaskmcp/) [Bearer Token](/agentkit/connectors/parallelaitaskmcp/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/snowflake.svg)](/agentkit/connectors/snowflake/) [Snowflake connector](/agentkit/connectors/snowflake/) [Connect to Snowflake to manage and analyze your data warehouse workloads](/agentkit/connectors/snowflake/) [OAuth 2.0](/agentkit/connectors/snowflake/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/snowflake.svg)](/agentkit/connectors/snowflakekeyauth/) [Snowflake Key Pair Auth connector](/agentkit/connectors/snowflakekeyauth/) [Connect to Snowflake via Public Private Key Pair to manage and analyze your data warehouse workloads](/agentkit/connectors/snowflakekeyauth/) [Bearer Token](/agentkit/connectors/snowflakekeyauth/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/supadata.svg)](/agentkit/connectors/supadata/) [Supadata connector](/agentkit/connectors/supadata/) [Connect with Supadata to extract transcripts, metadata, and structured content from YouTube, social media, and the web using AI.](/agentkit/connectors/supadata/) [API Key](/agentkit/connectors/supadata/) ## Data Visualization [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/tableau.svg)](/agentkit/connectors/tableau/) [Tableau connector](/agentkit/connectors/tableau/) [Connect to Tableau Cloud or Tableau Server to browse workbooks, views, and data sources, export visualizations, and query underlying data.](/agentkit/connectors/tableau/) [API Key](/agentkit/connectors/tableau/) ## Developer Tools [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/apify.svg)](/agentkit/connectors/apifymcp/) [Apify MCP connector](/agentkit/connectors/apifymcp/) [Connect to Apify MCP to run web scraping, browser automation, and data extraction Actors directly from your AI workflows.](/agentkit/connectors/apifymcp/) [Bearer Token](/agentkit/connectors/apifymcp/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/github.png)](/agentkit/connectors/github/) [Github connector](/agentkit/connectors/github/) [GitHub is a cloud-based Git repository hosting service that allows developers to store, manage, and track changes to their code.](/agentkit/connectors/github/) [OAuth 2.0](/agentkit/connectors/github/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/gitlab.svg)](/agentkit/connectors/gitlab/) [GitLab connector](/agentkit/connectors/gitlab/) [Connect to GitLab to manage repositories, issues, merge requests, pipelines, CI/CD, users, groups, and DevOps workflows.](/agentkit/connectors/gitlab/) [OAuth 2.0](/agentkit/connectors/gitlab/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/jira.svg)](/agentkit/connectors/jira/) [Jira connector](/agentkit/connectors/jira/) [Connect to Jira. Manage issues, projects, workflows, and agile development processes](/agentkit/connectors/jira/) [OAuth 2.0](/agentkit/connectors/jira/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/linear.svg)](/agentkit/connectors/linear/) [Linear connector](/agentkit/connectors/linear/) [Connect to Linear. Manage issues, projects, sprints, and development workflows](/agentkit/connectors/linear/) [OAuth 2.0](/agentkit/connectors/linear/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/pagerduty.svg)](/agentkit/connectors/pagerduty/) [PagerDuty connector](/agentkit/connectors/pagerduty/) [Connect to PagerDuty to manage incidents, services, users, teams, escalation policies, schedules, and on-call rotations.](/agentkit/connectors/pagerduty/) [OAuth 2.0](/agentkit/connectors/pagerduty/) [![](https://raw.githubusercontent.com/simple-icons/simple-icons/develop/icons/vercel.svg)](/agentkit/connectors/vercel/) [Vercel connector](/agentkit/connectors/vercel/) [Connect to Vercel. Access user profile, teams, projects, deployments, and environment settings.](/agentkit/connectors/vercel/) [OAuth 2.0](/agentkit/connectors/vercel/) ## Developer-Tools [![](https://cdn.scalekit.cloud/sk-connect/assets/provider-icons/parallel-ai.svg)](/agentkit/connectors/parallelaitaskmcp/) [Parallel AI Task MCP connector](/agentkit/connectors/parallelaitaskmcp/) [Connect to Parallel AI Task MCP to run deep research tasks and task groups directly from your AI workflows.](/agentkit/connectors/parallelaitaskmcp/) [Bearer Token](/agentkit/connectors/parallelaitaskmcp/) ## Development [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/bitbucket.svg)](/agentkit/connectors/bitbucket/) [Bitbucket connector](/agentkit/connectors/bitbucket/) [Connect to Bitbucket. Manage repositories, pipelines, pull requests, and code collaboration.](/agentkit/connectors/bitbucket/) [OAuth 2.0](/agentkit/connectors/bitbucket/) ## Documents [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/confluence.svg)](/agentkit/connectors/confluence/) [Confluence connector](/agentkit/connectors/confluence/) [Connect to Confluence. Manage spaces, pages, content, and team collaboration](/agentkit/connectors/confluence/) [OAuth 2.0](/agentkit/connectors/confluence/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/drop_box.svg)](/agentkit/connectors/dropbox/) [Dropbox connector](/agentkit/connectors/dropbox/) [Connect to Dropbox. Manage files, folders, sharing, and cloud storage workflows](/agentkit/connectors/dropbox/) [OAuth 2.0](/agentkit/connectors/dropbox/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/figma.svg)](/agentkit/connectors/figma/) [Figma connector](/agentkit/connectors/figma/) [Connect to Figma to access user files, teams, projects, and design metadata via OAuth 2.0](/agentkit/connectors/figma/) [OAuth 2.0](/agentkit/connectors/figma/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/google_docs.svg)](/agentkit/connectors/googledocs/) [Google Docs connector](/agentkit/connectors/googledocs/) [Connect to Google Docs. Create, edit, and collaborate on documents](/agentkit/connectors/googledocs/) [OAuth 2.0](/agentkit/connectors/googledocs/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/google_drive.svg)](/agentkit/connectors/googledrive/) [Google Drive connector](/agentkit/connectors/googledrive/) [Connect to Google Drive. Manage files, folders, and sharing permissions](/agentkit/connectors/googledrive/) [OAuth 2.0](/agentkit/connectors/googledrive/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/google_forms.svg)](/agentkit/connectors/googleforms/) [Google Forms connector](/agentkit/connectors/googleforms/) [Connect to Google Forms. Create, view, and manage forms and responses securely](/agentkit/connectors/googleforms/) [OAuth 2.0](/agentkit/connectors/googleforms/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/google_sheets.svg)](/agentkit/connectors/googlesheets/) [Google Sheets connector](/agentkit/connectors/googlesheets/) [Connect to Google Sheets. Create, edit, and analyze spreadsheets with powerful data management capabilities](/agentkit/connectors/googlesheets/) [OAuth 2.0](/agentkit/connectors/googlesheets/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/google_slides.svg)](/agentkit/connectors/googleslides/) [Google Slides connector](/agentkit/connectors/googleslides/) [Connect to Google Slides to create, read, and modify presentations programmatically.](/agentkit/connectors/googleslides/) [OAuth 2.0](/agentkit/connectors/googleslides/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/excel.svg)](/agentkit/connectors/microsoftexcel/) [Microsoft Excel connector](/agentkit/connectors/microsoftexcel/) [Connect to Microsoft Excel. Access, read, and modify spreadsheets stored in OneDrive or SharePoint through Microsoft Graph API.](/agentkit/connectors/microsoftexcel/) [OAuth 2.0](/agentkit/connectors/microsoftexcel/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/word.svg)](/agentkit/connectors/microsoftword/) [Microsoft Word connector](/agentkit/connectors/microsoftword/) [Connect to Microsoft Word. Authenticate with your Microsoft account to create, read, and edit Word documents stored in OneDrive or SharePoint through...](/agentkit/connectors/microsoftword/) [OAuth 2.0](/agentkit/connectors/microsoftword/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/notion.svg)](/agentkit/connectors/notion/) [Notion connector](/agentkit/connectors/notion/) [Connect to Notion workspace. Create, edit pages, manage databases, and collaborate on content](/agentkit/connectors/notion/) [OAuth 2.0](/agentkit/connectors/notion/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/onedrive.svg)](/agentkit/connectors/onedrive/) [OneDrive connector](/agentkit/connectors/onedrive/) [Connect to OneDrive. Manage files, folders, and cloud storage with Microsoft OneDrive](/agentkit/connectors/onedrive/) [OAuth 2.0](/agentkit/connectors/onedrive/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/onenote.svg)](/agentkit/connectors/onenote/) [OneNote connector](/agentkit/connectors/onenote/) [Connect to Microsoft OneNote. Access, create, and manage notebooks, sections, and pages stored in OneDrive or SharePoint through Microsoft Graph API.](/agentkit/connectors/onenote/) [OAuth 2.0](/agentkit/connectors/onenote/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/sharepoint.svg)](/agentkit/connectors/sharepoint/) [SharePoint connector](/agentkit/connectors/sharepoint/) [Connect to SharePoint. Manage sites, documents, lists, and collaborative content](/agentkit/connectors/sharepoint/) [OAuth 2.0](/agentkit/connectors/sharepoint/) ## Files [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/confluence.svg)](/agentkit/connectors/confluence/) [Confluence connector](/agentkit/connectors/confluence/) [Connect to Confluence. Manage spaces, pages, content, and team collaboration](/agentkit/connectors/confluence/) [OAuth 2.0](/agentkit/connectors/confluence/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/drop_box.svg)](/agentkit/connectors/dropbox/) [Dropbox connector](/agentkit/connectors/dropbox/) [Connect to Dropbox. Manage files, folders, sharing, and cloud storage workflows](/agentkit/connectors/dropbox/) [OAuth 2.0](/agentkit/connectors/dropbox/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/figma.svg)](/agentkit/connectors/figma/) [Figma connector](/agentkit/connectors/figma/) [Connect to Figma to access user files, teams, projects, and design metadata via OAuth 2.0](/agentkit/connectors/figma/) [OAuth 2.0](/agentkit/connectors/figma/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/google_docs.svg)](/agentkit/connectors/googledocs/) [Google Docs connector](/agentkit/connectors/googledocs/) [Connect to Google Docs. Create, edit, and collaborate on documents](/agentkit/connectors/googledocs/) [OAuth 2.0](/agentkit/connectors/googledocs/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/google_drive.svg)](/agentkit/connectors/googledrive/) [Google Drive connector](/agentkit/connectors/googledrive/) [Connect to Google Drive. Manage files, folders, and sharing permissions](/agentkit/connectors/googledrive/) [OAuth 2.0](/agentkit/connectors/googledrive/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/google_forms.svg)](/agentkit/connectors/googleforms/) [Google Forms connector](/agentkit/connectors/googleforms/) [Connect to Google Forms. Create, view, and manage forms and responses securely](/agentkit/connectors/googleforms/) [OAuth 2.0](/agentkit/connectors/googleforms/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/google_sheets.svg)](/agentkit/connectors/googlesheets/) [Google Sheets connector](/agentkit/connectors/googlesheets/) [Connect to Google Sheets. Create, edit, and analyze spreadsheets with powerful data management capabilities](/agentkit/connectors/googlesheets/) [OAuth 2.0](/agentkit/connectors/googlesheets/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/google_slides.svg)](/agentkit/connectors/googleslides/) [Google Slides connector](/agentkit/connectors/googleslides/) [Connect to Google Slides to create, read, and modify presentations programmatically.](/agentkit/connectors/googleslides/) [OAuth 2.0](/agentkit/connectors/googleslides/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/excel.svg)](/agentkit/connectors/microsoftexcel/) [Microsoft Excel connector](/agentkit/connectors/microsoftexcel/) [Connect to Microsoft Excel. Access, read, and modify spreadsheets stored in OneDrive or SharePoint through Microsoft Graph API.](/agentkit/connectors/microsoftexcel/) [OAuth 2.0](/agentkit/connectors/microsoftexcel/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/word.svg)](/agentkit/connectors/microsoftword/) [Microsoft Word connector](/agentkit/connectors/microsoftword/) [Connect to Microsoft Word. Authenticate with your Microsoft account to create, read, and edit Word documents stored in OneDrive or SharePoint through...](/agentkit/connectors/microsoftword/) [OAuth 2.0](/agentkit/connectors/microsoftword/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/notion.svg)](/agentkit/connectors/notion/) [Notion connector](/agentkit/connectors/notion/) [Connect to Notion workspace. Create, edit pages, manage databases, and collaborate on content](/agentkit/connectors/notion/) [OAuth 2.0](/agentkit/connectors/notion/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/onedrive.svg)](/agentkit/connectors/onedrive/) [OneDrive connector](/agentkit/connectors/onedrive/) [Connect to OneDrive. Manage files, folders, and cloud storage with Microsoft OneDrive](/agentkit/connectors/onedrive/) [OAuth 2.0](/agentkit/connectors/onedrive/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/onenote.svg)](/agentkit/connectors/onenote/) [OneNote connector](/agentkit/connectors/onenote/) [Connect to Microsoft OneNote. Access, create, and manage notebooks, sections, and pages stored in OneDrive or SharePoint through Microsoft Graph API.](/agentkit/connectors/onenote/) [OAuth 2.0](/agentkit/connectors/onenote/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/sharepoint.svg)](/agentkit/connectors/sharepoint/) [SharePoint connector](/agentkit/connectors/sharepoint/) [Connect to SharePoint. Manage sites, documents, lists, and collaborative content](/agentkit/connectors/sharepoint/) [OAuth 2.0](/agentkit/connectors/sharepoint/) ## Finance [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/dynamo.svg)](/agentkit/connectors/dynamo/) [Dynamo Software connector](/agentkit/connectors/dynamo/) [Connect to Dynamo Software API to access investment management, CRM, and reporting data.](/agentkit/connectors/dynamo/) [Bearer Token](/agentkit/connectors/dynamo/) ## Linkedin [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/heyreach.svg)](/agentkit/connectors/heyreach/) [HeyReach connector](/agentkit/connectors/heyreach/) [Connect to HeyReach to manage LinkedIn outreach campaigns, lead lists, and conversations. List campaigns, retrieve leads, monitor campaign progress, and...](/agentkit/connectors/heyreach/) [API Key](/agentkit/connectors/heyreach/) ## Media [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/diarize.svg)](/agentkit/connectors/diarize/) [Diarize connector](/agentkit/connectors/diarize/) [Connect to Diarize to transcribe and diarize audio and video content from YouTube, X, Instagram, and TikTok. Submit transcription jobs and retrieve...](/agentkit/connectors/diarize/) [Bearer Token](/agentkit/connectors/diarize/) ## Other [![]()](/agentkit/connectors/datadog/) [Datadog connector](/agentkit/connectors/datadog/) [Connect to Datadog to monitor metrics, logs, dashboards, monitors, incidents, SLOs, and more across your infrastructure.](/agentkit/connectors/datadog/) [OAuth 2.0](/agentkit/connectors/datadog/) [![]()](/agentkit/connectors/mailchimp/) [Mailchimp connector](/agentkit/connectors/mailchimp/) [Connect your agent to Mailchimp to manage subscribers, campaigns, lists, and email reports using OAuth 2.0.](/agentkit/connectors/mailchimp/) [OAuth 2.0](/agentkit/connectors/mailchimp/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/posthog-1.svg)](/agentkit/connectors/posthogmcp/) [Posthog MCP connector](/agentkit/connectors/posthogmcp/) [Connect to Posthog MCP to enable your AI agents and tools to directly interact with PostHog's products.](/agentkit/connectors/posthogmcp/) [OAuth 2.0](/agentkit/connectors/posthogmcp/) [![]()](/agentkit/connectors/quickbooks/) [QuickBooks connector](/agentkit/connectors/quickbooks/) [Connect your agent to QuickBooks Online to manage customers, invoices, bills, payments, and financial reports.](/agentkit/connectors/quickbooks/) [OAuth 2.0](/agentkit/connectors/quickbooks/) [![]()](/agentkit/connectors/xero/) [Xero connector](/agentkit/connectors/xero/) [Connect to Xero to manage invoices, contacts, payments, accounts, and financial reports via OAuth 2.0.](/agentkit/connectors/xero/) [OAuth 2.0](/agentkit/connectors/xero/) ## Outreach [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/heyreach.svg)](/agentkit/connectors/heyreach/) [HeyReach connector](/agentkit/connectors/heyreach/) [Connect to HeyReach to manage LinkedIn outreach campaigns, lead lists, and conversations. List campaigns, retrieve leads, monitor campaign progress, and...](/agentkit/connectors/heyreach/) [API Key](/agentkit/connectors/heyreach/) ## Productivity [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/box.svg)](/agentkit/connectors/box/) [Box connector](/agentkit/connectors/box/) [Box is a cloud content management platform. Manage files, folders, users, groups, collaborations, tasks, comments, webhooks, search, and more using the...](/agentkit/connectors/box/) [OAuth 2.0](/agentkit/connectors/box/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/calendly.svg)](/agentkit/connectors/calendly/) [Calendly connector](/agentkit/connectors/calendly/) [Connect to Calendly. Access user profile, events, and scheduling workflows.](/agentkit/connectors/calendly/) [OAuth 2.0](/agentkit/connectors/calendly/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/diarize.svg)](/agentkit/connectors/diarize/) [Diarize connector](/agentkit/connectors/diarize/) [Connect to Diarize to transcribe and diarize audio and video content from YouTube, X, Instagram, and TikTok. Submit transcription jobs and retrieve...](/agentkit/connectors/diarize/) [Bearer Token](/agentkit/connectors/diarize/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/Miro.svg)](/agentkit/connectors/miro/) [Miro connector](/agentkit/connectors/miro/) [Miro is a visual collaboration platform for teams. Manage boards, sticky notes, shapes, cards, frames, connectors, images, and tags using the Miro REST...](/agentkit/connectors/miro/) [OAuth 2.0](/agentkit/connectors/miro/) [![](https://cdn.scalekit.cloud/sk-connect/assets/provider-icons/parallel-ai.svg)](/agentkit/connectors/parallelaitaskmcp/) [Parallel AI Task MCP connector](/agentkit/connectors/parallelaitaskmcp/) [Connect to Parallel AI Task MCP to run deep research tasks and task groups directly from your AI workflows.](/agentkit/connectors/parallelaitaskmcp/) [Bearer Token](/agentkit/connectors/parallelaitaskmcp/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/tableau.svg)](/agentkit/connectors/tableau/) [Tableau connector](/agentkit/connectors/tableau/) [Connect to Tableau Cloud or Tableau Server to browse workbooks, views, and data sources, export visualizations, and query underlying data.](/agentkit/connectors/tableau/) [API Key](/agentkit/connectors/tableau/) ## Project Management [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/airtable.svg)](/agentkit/connectors/airtable/) [Airtable connector](/agentkit/connectors/airtable/) [Connect to Airtable. Manage databases, tables, records, and collaborate on structured data](/agentkit/connectors/airtable/) [OAuth 2.0](/agentkit/connectors/airtable/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/asana-n.svg)](/agentkit/connectors/asana/) [Asana connector](/agentkit/connectors/asana/) [Connect to Asana. Manage tasks, projects, teams, and workflow automation](/agentkit/connectors/asana/) [OAuth 2.0](/agentkit/connectors/asana/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/clickup.svg)](/agentkit/connectors/clickup/) [ClickUp connector](/agentkit/connectors/clickup/) [Connect to ClickUp. Manage tasks, projects, workspaces, and team collaboration](/agentkit/connectors/clickup/) [OAuth 2.0](/agentkit/connectors/clickup/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/confluence.svg)](/agentkit/connectors/confluence/) [Confluence connector](/agentkit/connectors/confluence/) [Connect to Confluence. Manage spaces, pages, content, and team collaboration](/agentkit/connectors/confluence/) [OAuth 2.0](/agentkit/connectors/confluence/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/jira.svg)](/agentkit/connectors/jira/) [Jira connector](/agentkit/connectors/jira/) [Connect to Jira. Manage issues, projects, workflows, and agile development processes](/agentkit/connectors/jira/) [OAuth 2.0](/agentkit/connectors/jira/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/linear.svg)](/agentkit/connectors/linear/) [Linear connector](/agentkit/connectors/linear/) [Connect to Linear. Manage issues, projects, sprints, and development workflows](/agentkit/connectors/linear/) [OAuth 2.0](/agentkit/connectors/linear/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/monday.svg)](/agentkit/connectors/monday/) [Monday.com connector](/agentkit/connectors/monday/) [Connect to Monday.com. Manage boards, tasks, workflows, teams, and project collaboration](/agentkit/connectors/monday/) [OAuth 2.0](/agentkit/connectors/monday/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/notion.svg)](/agentkit/connectors/notion/) [Notion connector](/agentkit/connectors/notion/) [Connect to Notion workspace. Create, edit pages, manage databases, and collaborate on content](/agentkit/connectors/notion/) [OAuth 2.0](/agentkit/connectors/notion/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/trello_n.svg)](/agentkit/connectors/trello/) [Trello connector](/agentkit/connectors/trello/) [Connect to Trello. Manage boards, cards, lists, and team collaboration workflows](/agentkit/connectors/trello/) [OAuth 1.0a](/agentkit/connectors/trello/) ## Sales [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/affinity.svg)](/agentkit/connectors/affinity/) [Affinity connector](/agentkit/connectors/affinity/) [Connect to Affinity relationship intelligence CRM to manage deal flow, relationships, pipeline opportunities, and network connections for private capital...](/agentkit/connectors/affinity/) [Bearer Token](/agentkit/connectors/affinity/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/apollo.svg)](/agentkit/connectors/apollo/) [Apollo connector](/agentkit/connectors/apollo/) [Connect to Apollo.io to search and enrich B2B contacts and accounts, manage CRM contacts, and automate outreach sequences.](/agentkit/connectors/apollo/) [OAuth 2.0](/agentkit/connectors/apollo/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/attention.svg)](/agentkit/connectors/attention/) [Attention connector](/agentkit/connectors/attention/) [Connect to Attention for AI insights, conversations, teams, and workflows](/agentkit/connectors/attention/) [API Key](/agentkit/connectors/attention/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/attio.svg)](/agentkit/connectors/attio/) [Attio connector](/agentkit/connectors/attio/) [Connect to Attio CRM to manage contacts, companies, deals, notes, tasks, and lists with a modern relationship management platform.](/agentkit/connectors/attio/) [OAuth 2.0](/agentkit/connectors/attio/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/chorus.svg)](/agentkit/connectors/chorus/) [Chorus connector](/agentkit/connectors/chorus/) [Connect to Chorus.ai to sync calls, transcripts, conversation intelligence, and analytics.](/agentkit/connectors/chorus/) [Basic Auth](/agentkit/connectors/chorus/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/clari.svg)](/agentkit/connectors/clari_copilot/) [Clari Copilot connector](/agentkit/connectors/clari_copilot/) [Connect to Clari Copilot for sales call transcripts, analytics, call data, and insights.](/agentkit/connectors/clari_copilot/) [API Key](/agentkit/connectors/clari_copilot/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/close.svg)](/agentkit/connectors/close/) [Close connector](/agentkit/connectors/close/) [Connect to Close CRM. Manage leads, contacts, opportunities, tasks, activities, and sales workflows](/agentkit/connectors/close/) [OAuth 2.0](/agentkit/connectors/close/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/dynamo.svg)](/agentkit/connectors/dynamo/) [Dynamo Software connector](/agentkit/connectors/dynamo/) [Connect to Dynamo Software API to access investment management, CRM, and reporting data.](/agentkit/connectors/dynamo/) [Bearer Token](/agentkit/connectors/dynamo/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/evertrace.png)](/agentkit/connectors/evertrace/) [Evertrace AI connector](/agentkit/connectors/evertrace/) [Connect to evertrace.ai to search and manage talent signals, saved searches, and lists. Access rich professional profiles with scoring, experiences, and...](/agentkit/connectors/evertrace/) [Bearer Token](/agentkit/connectors/evertrace/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/gong.svg)](/agentkit/connectors/gong/) [Gong connector](/agentkit/connectors/gong/) [Connect with Gong to sync calls, transcripts, insights, coaching and CRM activity](/agentkit/connectors/gong/) [OAuth 2.0](/agentkit/connectors/gong/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/google_ads.png)](/agentkit/connectors/google_ads/) [Google Ads connector](/agentkit/connectors/google_ads/) [Connect to Google Ads to manage advertising campaigns, analyze performance metrics, and optimize ad spending across Google's advertising platform](/agentkit/connectors/google_ads/) [OAuth 2.0](/agentkit/connectors/google_ads/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/harvestapi.svg)](/agentkit/connectors/harvestapi/) [HarvestAPI connector](/agentkit/connectors/harvestapi/) [Connect to HarvestAPI to scrape LinkedIn profiles, companies, and job listings, and search for people and jobs using LinkedIn data. Enables AI agents to...](/agentkit/connectors/harvestapi/) [API Key](/agentkit/connectors/harvestapi/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/heyreach.svg)](/agentkit/connectors/heyreach/) [HeyReach connector](/agentkit/connectors/heyreach/) [Connect to HeyReach to manage LinkedIn outreach campaigns, lead lists, and conversations. List campaigns, retrieve leads, monitor campaign progress, and...](/agentkit/connectors/heyreach/) [API Key](/agentkit/connectors/heyreach/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/hub_spot.svg)](/agentkit/connectors/hubspot/) [HubSpot connector](/agentkit/connectors/hubspot/) [Connect to HubSpot CRM. Manage contacts, deals, companies, and marketing automation](/agentkit/connectors/hubspot/) [OAuth 2.0](/agentkit/connectors/hubspot/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/jiminny.svg)](/agentkit/connectors/jiminny/) [Jiminny connector](/agentkit/connectors/jiminny/) [Connect with Jiminny to access call recordings, transcripts, coaching insights, and conversation intelligence data.](/agentkit/connectors/jiminny/) [Bearer Token](/agentkit/connectors/jiminny/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/linkedin.svg)](/agentkit/connectors/linkedin/) [LinkedIn connector](/agentkit/connectors/linkedin/) [Connect to LinkedIn to manage user authentication, profile data, email, and professional identity via OAuth 2.0](/agentkit/connectors/linkedin/) [OAuth 2.0](/agentkit/connectors/linkedin/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/outreach.png)](/agentkit/connectors/outreach/) [Outreach connector](/agentkit/connectors/outreach/) [Connect with Outreach to manage prospects, accounts, sequences, emails, calls, and sales engagement workflows.](/agentkit/connectors/outreach/) [OAuth 2.0](/agentkit/connectors/outreach/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/pipedrive.svg)](/agentkit/connectors/pipedrive/) [Pipedrive connector](/agentkit/connectors/pipedrive/) [Connect to Pipedrive CRM. Manage deals, contacts, organizations, activities, leads, and notes to streamline your sales pipeline.](/agentkit/connectors/pipedrive/) [OAuth 2.0](/agentkit/connectors/pipedrive/) [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/sales_force.svg)](/agentkit/connectors/salesforce/) [Salesforce connector](/agentkit/connectors/salesforce/) [Connect to Salesforce CRM. Manage leads, opportunities, accounts, and customer relationships](/agentkit/connectors/salesforce/) [OAuth 2.0](/agentkit/connectors/salesforce/) ## Scheduling [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/calendly.svg)](/agentkit/connectors/calendly/) [Calendly connector](/agentkit/connectors/calendly/) [Connect to Calendly. Access user profile, events, and scheduling workflows.](/agentkit/connectors/calendly/) [OAuth 2.0](/agentkit/connectors/calendly/) ## Storage [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/box.svg)](/agentkit/connectors/box/) [Box connector](/agentkit/connectors/box/) [Box is a cloud content management platform. Manage files, folders, users, groups, collaborations, tasks, comments, webhooks, search, and more using the...](/agentkit/connectors/box/) [OAuth 2.0](/agentkit/connectors/box/) ## Transcription [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/diarize.svg)](/agentkit/connectors/diarize/) [Diarize connector](/agentkit/connectors/diarize/) [Connect to Diarize to transcribe and diarize audio and video content from YouTube, X, Instagram, and TikTok. Submit transcription jobs and retrieve...](/agentkit/connectors/diarize/) [Bearer Token](/agentkit/connectors/diarize/) ## Version Control [![](https://cdn.scalekit.com/sk-connect/assets/provider-icons/bitbucket.svg)](/agentkit/connectors/bitbucket/) [Bitbucket connector](/agentkit/connectors/bitbucket/) [Connect to Bitbucket. Manage repositories, pipelines, pull requests, and code collaboration.](/agentkit/connectors/bitbucket/) [OAuth 2.0](/agentkit/connectors/bitbucket/) No providers match your search. --- # DOCUMENT BOUNDARY --- # Affinity connector > Connect to Affinity relationship intelligence CRM to manage deal flow, relationships, pipeline opportunities, and network connections for private capital... 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Make your first call [Section titled “Make your first call”](#make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'affinity' 12 const identifier = 'user_123' 13 14 // Make your first call 15 const result = await actions.executeTool({ 16 connector, 17 identifier, 18 toolName: 'affinity_list_lists', 19 toolInput: {}, 20 }) 21 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "affinity" 14 identifier = "user_123" 15 16 # Make your first call 17 result = actions.execute_tool( 18 tool_input={}, 19 tool_name="affinity_list_lists", 20 connection_name=connection_name, 21 identifier=identifier, 22 ) 23 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Create note, opportunity** — Create a note on a person, organization, or opportunity in Affinity * **Get opportunity, relationship strength, organization** — Retrieve full details of a deal or opportunity in Affinity including current stage, owner, associated persons and organizations, custom field values, and list membership * **List opportunities, lists, notes** — List pipeline opportunities in Affinity with optional filters by list ID, owner, or stage * **Search persons, organizations** — Search for people in the Affinity network by name, email, or relationship strength * **Update opportunity** — Update an existing deal or opportunity in Affinity ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Airtable connector > Connect to Airtable. Manage databases, tables, records, and collaborate on structured data 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Airtable credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'airtable' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Airtable:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first API call through the proxy 21 const result = await actions.request({ 22 connectionName: connector, 23 identifier, 24 path: '/v0/meta/whoami', 25 method: 'GET', 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "airtable" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Airtable:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first API call through the proxy 25 result = actions.request( 26 connection_name=connection_name, 27 identifier=identifier, 28 path="/v0/meta/whoami", 29 method="GET", 30 ) 31 print(result) ``` ## Common workflows [Section titled “Common workflows”](#common-workflows) --- # DOCUMENT BOUNDARY --- # Apify MCP connector > Connect to Apify MCP to run web scraping, browser automation, and data extraction Actors directly from your AI workflows. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Apify MCP credentials with Scalekit so it can authenticate requests on your behalf. You do this once per environment. 4. ### Make your first call [Section titled “Make your first call”](#make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'apifymcp' 12 const identifier = 'user_123' 13 14 // Make your first call 15 const result = await actions.executeTool({ 16 connector, 17 identifier, 18 toolName: 'apifymcp_search_actors', 19 toolInput: {}, 20 }) 21 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "apifymcp" 14 identifier = "user_123" 15 16 # Make your first call 17 result = actions.execute_tool( 18 tool_input={}, 19 tool_name="apifymcp_search_actors", 20 connection_name=connection_name, 21 identifier=identifier, 22 ) 23 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Get actor run, actor output** — Get detailed information about a specific Actor run by runId * **Fetch actor details, apify docs** — Get detailed information about an Actor by its ID or full name (format: ‘username/name’, e.g * **Search actors, apify docs** — Search the Apify Store to FIND and DISCOVER what scraping tools/Actors exist for specific platforms or use cases * **Actor call** — Call any Actor from the Apify Store * **Browser rag web** — Web browser for AI agents and RAG pipelines ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Apollo connector > Connect to Apollo.io to search and enrich B2B contacts and accounts, manage CRM contacts, and automate outreach sequences. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Apollo credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'apollo' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Apollo:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'apollo_list_sequences', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "apollo" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Apollo:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="apollo_list_sequences", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Create account, contact** — Create a new account (company) record in your Apollo CRM * **List sequences** — List available email sequences (Apollo Sequences / Emailer Campaigns) in your Apollo account * **Update contact** — Update properties or CRM stage of an existing Apollo contact record by contact ID * **Get account, contact** — Retrieve the full profile of a company account from Apollo by its ID * **Contact enrich** — Enrich a contact using Apollo’s people matching engine * **Search contacts, accounts** — Search contacts in your Apollo CRM using filters such as job title, company, and sort order ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Asana connector > Connect to Asana. Manage tasks, projects, teams, and workflow automation 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Asana credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'asana' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Asana:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'asana_me_get', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "asana" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Asana:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="asana_me_get", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **List workspaces, workspace teams, webhooks** — List all workspaces the authenticated user has access to * **Get workspace, user, team** — Get details of a specific workspace by its GID * **User team remove, team add** — Remove a user from a team * **Update task, tag, section** — Update an existing task’s properties * **Parent task set** — Set or change the parent task of a task * **Tag task remove, task add** — Remove a tag from a task ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Attention connector > Connect to Attention for AI insights, conversations, teams, and workflows 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Make your first call [Section titled “Make your first call”](#make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'attention' 12 const identifier = 'user_123' 13 14 // Make your first API call through the proxy 15 const result = await actions.request({ 16 connectionName: connector, 17 identifier, 18 path: '/v1/users/me', 19 method: 'GET', 20 }) 21 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "attention" 14 identifier = "user_123" 15 16 # Make your first API call through the proxy 17 result = actions.request( 18 connection_name=connection_name, 19 identifier=identifier, 20 path="/v1/users/me", 21 method="GET", 22 ) 23 print(result) ``` ## Common workflows [Section titled “Common workflows”](#common-workflows) --- # DOCUMENT BOUNDARY --- # Attio connector > Connect to Attio CRM to manage contacts, companies, deals, notes, tasks, and lists with a modern relationship management platform. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Attio credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'attio' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Attio:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'attio_get_current_token_info', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "attio" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Attio:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="attio_get_current_token_info", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Update record** — Update an existing record’s attributes in Attio * **Create person, task, comment** — Creates a new person record in Attio * **List records, people, objects** — List and query records for a specific Attio object type (e.g * **Search records** — Search for records in Attio for a given object type (people, companies, deals, or custom objects) using a fuzzy text query * **Delete person, user record, record** — Permanently deletes a person record from Attio by its record\_id * **Get comment, record attribute values, attribute** — Retrieves a single comment by its comment\_id in Attio ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Google BigQuery connector > BigQuery is Google Cloud’s fully-managed enterprise data warehouse for analytics at scale. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Google BigQuery credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'bigquery' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Google BigQuery:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first API call through the proxy 21 const result = await actions.request({ 22 connectionName: connector, 23 identifier, 24 path: '/bigquery/v2/projects', 25 method: 'GET', 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "bigquery" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Google BigQuery:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first API call through the proxy 25 result = actions.request( 26 connection_name=connection_name, 27 identifier=identifier, 28 path="/bigquery/v2/projects", 29 method="GET", 30 ) 31 print(result) ``` ## Common workflows [Section titled “Common workflows”](#common-workflows) --- # DOCUMENT BOUNDARY --- # BigQuery (Service Account) connector > Connect to Google BigQuery using a GCP service account for server-to-server authentication without user login. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your BigQuery (Service Account) credentials with Scalekit so it handles the token lifecycle. You do this once per environment. ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Query insert** — Submit an asynchronous BigQuery query job * **Job cancel** — Request cancellation of a running BigQuery job * **Run dry, query** — Validate a SQL query and estimate its cost without executing it * **List tables, table data, routines** — List all tables and views in a BigQuery dataset * **Get table, routine, query results** — Retrieve metadata and schema for a specific BigQuery table or view, including column names, types, descriptions, and table properties ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Bitbucket connector > Connect to Bitbucket. Manage repositories, pipelines, pull requests, and code collaboration. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Bitbucket credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'bitbucket' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Bitbucket:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'bitbucket_user_emails_list', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "bitbucket" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Bitbucket:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="bitbucket_user_emails_list", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Get commit comment, workspace, merge base** — Returns a specific comment on a commit * **Search workspace** — Searches for code across all repositories in a workspace * **Delete workspace pipeline variable, deploy key, repository permission user** — Deletes a workspace pipeline variable * **Create tag, environment, commit build status** — Creates a new tag in a Bitbucket repository pointing to a specific commit * **Update pull request task, deployment variable, commit build status** — Updates a task on a pull request (e.g * **Unwatch issue** — Stops watching an issue ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Box connector > Box is a cloud content management platform. Manage files, folders, users, groups, collaborations, tasks, comments, webhooks, search, and more using the... 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Box credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'box' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Box:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'box_collections_list', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "box" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Box:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="box_collections_list", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Get file representations, webhook, web link** — Retrieves available representations for a file, such as thumbnails, PDFs, or extracted text * **List webhooks, users, user memberships** — Retrieves all webhooks for the application * **Update webhook, web link, user** — Updates a webhook’s address or triggers * **Delete webhook, web link, user** — Removes a webhook * **Create webhook, web link, user** — Creates a webhook to receive event notifications * **Restore trash folder, trash file** — Restores a folder from the trash ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Brave Search connector > Connect to Brave Search to perform web, image, video, and news searches with privacy-focused results, plus AI-powered suggestions and spellcheck. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Make your first call [Section titled “Make your first call”](#make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'brave' 12 const identifier = 'user_123' 13 14 // Make your first call 15 const result = await actions.executeTool({ 16 connector, 17 identifier, 18 toolName: 'brave_local_place_search', 19 toolInput: {}, 20 }) 21 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "brave" 14 identifier = "user_123" 15 16 # Make your first call 17 result = actions.execute_tool( 18 tool_input={}, 19 tool_name="brave_local_place_search", 20 connection_name=connection_name, 21 identifier=identifier, 22 ) 23 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Descriptions local** — Fetch AI-generated descriptions for locations using IDs from a Brave web search response * **Summary summarizer** — Fetch the complete AI-generated summary for a summarizer key * **Search web, local place, image** — Search the web using Brave Search’s privacy-focused search engine * **Completions chat** — Get AI-generated answers grounded in real-time Brave Search results using an OpenAI-compatible chat completions interface * **Pois local** — Fetch detailed Point of Interest (POI) data for up to 20 location IDs returned by a Brave web search response * **Enrichments summarizer** — Fetch enrichment data for a Brave AI summary key ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Calendly connector > Connect to Calendly. Access user profile, events, and scheduling workflows. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Calendly credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'calendly' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Calendly:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'calendly_current_user_get', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "calendly" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Calendly:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="calendly_current_user_get", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Delete webhook subscription, data compliance events, data compliance invitees** — Deletes a Calendly webhook subscription, stopping future event notifications * **List event type availability schedules, group relationships, groups** — Returns a list of availability schedules for the specified Calendly event type * **Create invitee no show, organization invitation, share** — Marks a specific invitee as a no-show for a scheduled Calendly event * **Get sample webhook data, organization membership, organization invitation** — Returns a sample webhook payload for the specified event type, useful for testing webhook integrations * **Update event type availability schedules, event type** — Updates the availability schedules (rules) for the specified Calendly event type * **Revoke organization invitation** — Revokes a pending invitation to a Calendly organization ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Chorus connector > Connect to Chorus.ai to sync calls, transcripts, conversation intelligence, and analytics. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Make your first call [Section titled “Make your first call”](#make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'chorus' 12 const identifier = 'user_123' 13 14 // Make your first API call through the proxy 15 const result = await actions.request({ 16 connectionName: connector, 17 identifier, 18 path: '/v1/users/me', 19 method: 'GET', 20 }) 21 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "chorus" 14 identifier = "user_123" 15 16 # Make your first API call through the proxy 17 result = actions.request( 18 connection_name=connection_name, 19 identifier=identifier, 20 path="/v1/users/me", 21 method="GET", 22 ) 23 print(result) ``` ## Common workflows [Section titled “Common workflows”](#common-workflows) --- # DOCUMENT BOUNDARY --- # Clari Copilot connector > Connect to Clari Copilot for sales call transcripts, analytics, call data, and insights. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Make your first call [Section titled “Make your first call”](#make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'clari-copilot' 12 const identifier = 'user_123' 13 14 // Make your first API call through the proxy 15 const result = await actions.request({ 16 connectionName: connector, 17 identifier, 18 path: '/v1/users/me', 19 method: 'GET', 20 }) 21 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "clari-copilot" 14 identifier = "user_123" 15 16 # Make your first API call through the proxy 17 result = actions.request( 18 connection_name=connection_name, 19 identifier=identifier, 20 path="/v1/users/me", 21 method="GET", 22 ) 23 print(result) ``` ## Common workflows [Section titled “Common workflows”](#common-workflows) --- # DOCUMENT BOUNDARY --- # ClickUp connector > Connect to ClickUp. Manage tasks, projects, workspaces, and team collaboration 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your ClickUp credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'clickup' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize ClickUp:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first API call through the proxy 21 const result = await actions.request({ 22 connectionName: connector, 23 identifier, 24 path: '/api/v2/user', 25 method: 'GET', 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "clickup" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize ClickUp:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first API call through the proxy 25 result = actions.request( 26 connection_name=connection_name, 27 identifier=identifier, 28 path="/api/v2/user", 29 method="GET", 30 ) 31 print(result) ``` ## Common workflows [Section titled “Common workflows”](#common-workflows) --- # DOCUMENT BOUNDARY --- # Close connector > Connect to Close CRM. Manage leads, contacts, opportunities, tasks, activities, and sales workflows 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Close credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'close' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Close:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'close_activities_list', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "close" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Close:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="close_activities_list", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **List webhooks, users, tasks** — List all webhook subscriptions in Close * **Update webhook, task, sms** — Update a webhook subscription’s URL or event subscriptions * **Get webhook, user, task** — Retrieve a single webhook subscription by ID * **Delete webhook, task, sms** — Delete a webhook subscription from Close * **Create webhook, task, sms** — Create a new webhook subscription to receive Close event notifications * **Merge lead** — Merge two leads into one ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Confluence connector > Connect to Confluence. Manage spaces, pages, content, and team collaboration 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Confluence credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'confluence' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Confluence:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first API call through the proxy 21 const result = await actions.request({ 22 connectionName: connector, 23 identifier, 24 path: '/wiki/rest/api/user/current', 25 method: 'GET', 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "confluence" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Confluence:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first API call through the proxy 25 result = actions.request( 26 connection_name=connection_name, 27 identifier=identifier, 28 path="/wiki/rest/api/user/current", 29 method="GET", 30 ) 31 print(result) ``` ## Common workflows [Section titled “Common workflows”](#common-workflows) --- # DOCUMENT BOUNDARY --- # Databricks Workspace connector > Connect to Databricks Workspace APIs using a Service Principal with OAuth 2.0 client credentials to manage clusters, jobs, notebooks, SQL, and more. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Schemata information schema** — List all schemas within a catalog using INFORMATION\_SCHEMA.SCHEMATA * **Constraints information schema table** — List PRIMARY KEY and FOREIGN KEY constraints for tables in a schema using INFORMATION\_SCHEMA.TABLE\_CONSTRAINTS * **List unity catalog schemas, unity catalog catalogs, unity catalog tables** — List all schemas within a Unity Catalog in the Databricks workspace * **Get sql statement result chunk, sql warehouse, sql statement** — Fetch a specific result chunk for a paginated SQL statement result * **Tables information schema** — List tables and views in a schema using INFORMATION\_SCHEMA.TABLES * **Columns information schema** — List columns for a table using INFORMATION\_SCHEMA.COLUMNS ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Datadog connector > Connect to Datadog to monitor metrics, logs, dashboards, monitors, incidents, SLOs, and more across your infrastructure. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Datadog API credentials with Scalekit so it stores them securely. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'datadog' 12 const identifier = 'user_123' 13 14 // Make your first call 15 const result = await actions.executeTool({ 16 connector, 17 identifier, 18 toolName: 'datadog_dashboards_list', 19 toolInput: {}, 20 }) 21 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "datadog" 14 identifier = "user_123" 15 16 # Make your first call 17 result = actions.execute_tool( 18 tool_input={}, 19 tool_name="datadog_dashboards_list", 20 connection_name=connection_name, 21 identifier=identifier, 22 ) 23 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Monitor infrastructure** — list, create, update, and delete monitors; mute and unmute alerts; manage downtime schedules * **Query metrics** — fetch timeseries data, list metric metadata and tags, submit custom metrics * **Search logs** — search and aggregate log events; list log indexes and pipelines * **Manage incidents** — create and retrieve incidents for incident response workflows * **Track SLOs** — create, update, delete, and get history for service level objectives * **Build dashboards** — create, update, delete, and list dashboards; capture graph snapshots * **Run Synthetics** — trigger and manage synthetic tests; get test results; manage locations and global variables * **Manage RUM** — create and list Real User Monitoring applications * **Manage notebooks** — create, retrieve, and delete collaborative notebooks * **Manage users and roles** — create users, assign roles, list permissions * **Monitor hosts and containers** — list hosts, mute/unmute hosts, manage host tags, list containers and processes * **Post events** — create and retrieve events in the Datadog event stream * **Run service checks** — submit custom service check results ## Common workflows [Section titled “Common workflows”](#common-workflows) Downtime response includes two IDs The `downtime_create` response contains both `data.id` (the downtime UUID) and `included[].id` (the creator’s user UUID). Always use `data.id` for subsequent `downtime_get`, `downtime_update`, and `downtime_cancel` calls. ## Getting resource IDs [Section titled “Getting resource IDs”](#getting-resource-ids) Most tools require IDs that must be fetched from the API — never guess or hard-code them. | Resource | Tool to get ID | Field in response | | --------------- | ---------------------------------- | ------------------------------------------------------------ | | Monitor ID | `datadog_monitors_list` | `array[].id` | | Dashboard ID | `datadog_dashboards_list` | `dashboards[].id` | | Downtime ID | `datadog_downtime_create` response | `data.id` (UUID — not `included[].id`) | | Notebook ID | `datadog_notebooks_list` | `data[].id` | | Incident ID | `datadog_incidents_list` | `data[].id` | | SLO ID | `datadog_slos_list` | `data[].id` | | Role ID | `datadog_roles_list` | `data[].id` | | User ID | `datadog_users_list` | `data[].id` | | RUM App ID | `datadog_rum_applications_list` | `data[].id` | | Event ID | `datadog_event_create` response | `event.id_str` (**use `id_str`, not `id`** — see note below) | | Metric name | `datadog_metrics_list` | `metrics[]` (requires `from` Unix timestamp) | | Log pipeline ID | `datadog_log_pipelines_list` | `array[].id` | ## Tool list [Section titled “Tool list”](#tool-list) Pass the exact tool name from the list below when you call `executeTool` (Node.js) or `execute_tool` (Python). --- # DOCUMENT BOUNDARY --- # Diarize connector > Connect to Diarize to transcribe and diarize audio and video content from YouTube, X, Instagram, and TikTok. Submit transcription jobs and retrieve... 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Diarize credentials with Scalekit so it can authenticate requests on your behalf. You do this once per environment. 4. ### Make your first call [Section titled “Make your first call”](#make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'diarize' 12 const identifier = 'user_123' 13 14 // Make your first call 15 const result = await actions.executeTool({ 16 connector, 17 identifier, 18 toolName: 'diarize_get_job_status', 19 toolInput: { job_id: 'YOUR_JOB_ID' }, 20 }) 21 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "diarize" 14 identifier = "user_123" 15 16 # Make your first call 17 result = actions.execute_tool( 18 tool_input={"job_id":"YOUR_JOB_ID"}, 19 tool_name="diarize_get_job_status", 20 connection_name=connection_name, 21 identifier=identifier, 22 ) 23 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Get job status** — Retrieve the current status of a transcription job by its job ID * **Transcript download** — Download the transcript output for a completed transcription job in JSON, TXT, SRT, or VTT format, including speaker diarization, segments, and word-level timestamps * **Create transcription job** — Submit a new transcription and diarization job for an audio or video URL (YouTube, X, Instagram, TikTok) ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Discord connector > Connect to Discord. Read user profile, guilds, roles, manage bots, and perform interactions. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Discord credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'discord' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Discord:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'discord_get_gateway', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "discord" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Discord:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="discord_get_gateway", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Get guild widget png, current user application entitlements, guild widget** — Retrieves a PNG image widget for a Discord guild * **List my guilds, sticker packs** — Lists the current user’s guilds, returning partial data (id, name, icon, owner, permissions, features) for each * **Invite resolve** — Resolves and retrieves information about a Discord invite code, including the associated guild, channel, event, and inviter * **Connections retrieve user** — Retrieves a list of the authenticated user’s connected third-party accounts on Discord, such as Twitch, YouTube, GitHub, Steam, and others ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Dropbox connector > Connect to Dropbox. Manage files, folders, sharing, and cloud storage workflows 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Dropbox credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'dropbox' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Dropbox:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first API call through the proxy 21 const result = await actions.request({ 22 connectionName: connector, 23 identifier, 24 path: '/2/users/get_current_account', 25 method: 'POST', 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "dropbox" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Dropbox:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first API call through the proxy 25 result = actions.request( 26 connection_name=connection_name, 27 identifier=identifier, 28 path="/2/users/get_current_account", 29 method="POST", 30 ) 31 print(result) ``` ## Common workflows [Section titled “Common workflows”](#common-workflows) --- # DOCUMENT BOUNDARY --- # Dynamo Software connector > Connect to Dynamo Software API to access investment management, CRM, and reporting data. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Make your first call [Section titled “Make your first call”](#make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'dynamo' 12 const identifier = 'user_123' 13 14 // Make your first call 15 const result = await actions.executeTool({ 16 connector, 17 identifier, 18 toolName: 'dynamo_get_document_extended_schema', 19 toolInput: {}, 20 }) 21 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "dynamo" 14 identifier = "user_123" 15 16 # Make your first call 17 result = actions.execute_tool( 18 tool_input={}, 19 tool_name="dynamo_get_document_extended_schema", 20 connection_name=connection_name, 21 identifier=identifier, 22 ) 23 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Search records** — Retrieves data matching saved search criteria from Dynamo using advanced filter queries * **Get view sql, document schema, view** — Returns data from a specific SQL view in Dynamo using the view name * **Delete entity, bulk** — Deletes a single instance of the specified Dynamo entity by ID * **Total entity** — Returns total count of items for a given Dynamo entity * **Id entity by** — Returns a single instance of a Dynamo entity by its ID with optional column selection and formatting controls * **Key reset api** — Removes the user’s API key from the server cache ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Evertrace AI connector > Connect to evertrace.ai to search and manage talent signals, saved searches, and lists. Access rich professional profiles with scoring, experiences, and... 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Make your first call [Section titled “Make your first call”](#make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'evertrace' 12 const identifier = 'user_123' 13 14 // Make your first call 15 const result = await actions.executeTool({ 16 connector, 17 identifier, 18 toolName: 'evertrace_cities_list', 19 toolInput: {}, 20 }) 21 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "evertrace" 14 identifier = "user_123" 15 16 # Make your first call 17 result = actions.execute_tool( 18 tool_input={}, 19 tool_name="evertrace_cities_list", 20 connection_name=connection_name, 21 identifier=identifier, 22 ) 23 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **List companies, entries delete, entries get** — Search companies by name or look up by specific IDs * **Delete lists, searches** — Permanently delete a list and all its entries * **Update lists, searches** — Rename a list * **Get lists, signals, searches** — Get a list by ID with its entries, accesses, and creator information * **Create lists, searches** — Create a new list * **Viewed signal mark** — Mark a signal as viewed by the current user ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Exa connector > Connect to Exa to perform AI-powered semantic web search, crawl websites for structured content, get natural language answers from the web, run in-depth... 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Exa credentials with Scalekit so it can authenticate requests on your behalf. You do this once per environment. 4. ### Make your first call [Section titled “Make your first call”](#make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'exa' 12 const identifier = 'user_123' 13 14 // Make your first call 15 const result = await actions.executeTool({ 16 connector, 17 identifier, 18 toolName: 'exa_list_websets', 19 toolInput: {}, 20 }) 21 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "exa" 14 identifier = "user_123" 15 16 # Make your first call 17 result = actions.execute_tool( 18 tool_input={}, 19 tool_name="exa_list_websets", 20 connection_name=connection_name, 21 identifier=identifier, 22 ) 23 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Similar find** — Find web pages similar to a given URL using Exa’s neural similarity search * **Search records** — Search the web using Exa’s AI-powered semantic or keyword search engine * **Research records** — Run in-depth research on a topic using Exa’s neural search * **Crawl records** — Crawl one or more web pages by URL and extract their content including full text, highlights, and AI-generated summaries * **List websets, webset items** — List all Exa Websets in your account with optional pagination * **Websets records** — Execute a complex web query designed to discover and return large sets of URLs (up to thousands) matching specific criteria ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Fathom connector > Connect to Fathom AI meeting assistant. Record, transcribe, and summarize meetings with AI-powered insights 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Make your first call [Section titled “Make your first call”](#make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'fathom' 12 const identifier = 'user_123' 13 14 // Make your first API call through the proxy 15 const result = await actions.request({ 16 connectionName: connector, 17 identifier, 18 path: '/v1/users/me', 19 method: 'GET', 20 }) 21 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "fathom" 14 identifier = "user_123" 15 16 # Make your first API call through the proxy 17 result = actions.request( 18 connection_name=connection_name, 19 identifier=identifier, 20 path="/v1/users/me", 21 method="GET", 22 ) 23 print(result) ``` ## Common workflows [Section titled “Common workflows”](#common-workflows) --- # DOCUMENT BOUNDARY --- # Figma connector > Connect to Figma to access user files, teams, projects, and design metadata via OAuth 2.0 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Figma credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'figma' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Figma:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'figma_activity_logs_list', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "figma" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Figma:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="figma_activity_logs_list", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Delete comment reaction, dev resource, file comment** — Removes the authenticated user’s emoji reaction from a comment in a Figma file * **List file components, file component sets, file styles** — Returns a list of all published components in a Figma file, including their keys, names, descriptions, and thumbnails * **Create file comment, webhook, comment reaction** — Posts a new comment on a Figma file * **Get webhook, file variables local, file image fills** — Returns details of a specific Figma webhook by its ID, including event type, endpoint, and status * **Update file variables, webhook, dev resource** — Creates, updates, or deletes variables and variable collections in a Figma file * **Render file images** — Renders nodes from a Figma file as images (PNG, JPG, SVG, or PDF) and returns URLs to download them ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Freshdesk connector > Connect to Freshdesk. Manage tickets, contacts, companies, and customer support workflows 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Reply tickets** — Add a public reply to a ticket conversation * **Get ticket** — Retrieve details of a specific ticket by ID * **Update ticket** — Update an existing ticket in Freshdesk * **Create ticket, agent, contact** — Create a new ticket in Freshdesk * **List tickets, roles, agents** — Retrieve a list of tickets with filtering and pagination * **Delete agent** — Delete an agent from Freshdesk ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Github connector > GitHub is a cloud-based Git repository hosting service that allows developers to store, manage, and track changes to their code. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Github credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'github' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Github:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'github_user_repos_list', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "github" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Github:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="github_user_repos_list", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Read repositories** — fetch repo metadata, files, commits, branches, and tags * **Manage issues** — create, update, close, and comment on issues * **Work with pull requests** — open PRs, post reviews, and merge changes * **Search code** — search across repositories by keyword, language, or file path * **Trigger workflows** — dispatch GitHub Actions workflow runs ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # GitLab connector > Connect to GitLab to manage repositories, issues, merge requests, pipelines, CI/CD, users, groups, and DevOps workflows. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your GitLab credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'gitlab' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize GitLab:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'gitlab_current_user_get', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "gitlab" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize GitLab:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="gitlab_current_user_get", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Get branch, milestone, user** — Get details of a specific branch in a GitLab repository * **Unstar project** — Unstar a GitLab project * **List merge request commits, namespaces, issue labels** — List commits in a specific merge request * **Search project, global** — Search within a specific GitLab project for issues, merge requests, commits, code, and more * **Create label, deploy key, project variable** — Create a new label in a GitLab project * **Delete milestone, tag, project** — Delete a milestone from a GitLab project ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Gmail connector > Gmail is Google's cloud based email service that allows you to access your messages from any computer or device with just a web browser. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Gmail credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'gmail' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Gmail:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'gmail_fetch_mails', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "gmail" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Gmail:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="gmail_fetch_mails", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Read emails** — fetch messages, threads, and attachments from any label or inbox * **Send and reply** — compose new emails and reply to existing threads on behalf of your users * **Search messages** — query Gmail with full search syntax to find emails by sender, subject, or content * **Manage labels** — apply, remove, and list labels to organize messages * **Access contacts** — look up contacts and people from the user’s address book ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Gong connector > Connect with Gong to sync calls, transcripts, insights, coaching and CRM activity 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Gong credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'gong' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Gong:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'gong_call_outcomes_list', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "gong" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Gong:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="gong_call_outcomes_list", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **List engage tasks, engage workspaces, engage flow folders** — List Gong Engage tasks for a specified user, such as call tasks, email tasks, LinkedIn tasks, and other follow-up actions * **Get users, calls transcript, library folder content** — Get detailed user information for specific Gong users using an extensive filter * **Complete engage task** — Mark a specific Gong Engage task as completed * **Unassign engage prospects** — Unassign CRM prospects (contacts or leads) from a specific Gong Engage flow using their CRM IDs, removing them from the flow sequence * **Override engage flow content, engage prospects assign cool off** — Override field placeholder values in a Gong Engage flow for specific prospects, allowing personalized content without modifying the base flow template * **Report engage email activity** — Report email engagement events (opens, clicks, bounces, unsubscribes) to Gong Engage so they appear in the activity timeline for a prospect ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Google Ads connector > Connect to Google Ads to manage advertising campaigns, analyze performance metrics, and optimize ad spending across Google's advertising platform 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Google Ads credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'google-ads' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Google Ads:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first API call through the proxy 21 const result = await actions.request({ 22 connectionName: connector, 23 identifier, 24 path: '/v17/customers', 25 method: 'GET', 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "google-ads" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Google Ads:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first API call through the proxy 25 result = actions.request( 26 connection_name=connection_name, 27 identifier=identifier, 28 path="/v17/customers", 29 method="GET", 30 ) 31 print(result) ``` ## Common workflows [Section titled “Common workflows”](#common-workflows) --- # DOCUMENT BOUNDARY --- # Google Calendar connector > Google Calendar is Google's cloud-based calendar service that allows you to manage your events, appointments, and schedules from any computer or device... 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Google Calendar credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'googlecalendar' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Google Calendar:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'googlecalendar_list_calendars', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "googlecalendar" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Google Calendar:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="googlecalendar_list_calendars", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Update event** — Update an existing event in a connected Google Calendar account * **List events, calendars** — List events from a connected Google Calendar account with filtering options * **Get event by id** — Retrieve a specific calendar event by its ID using optional filtering and list parameters * **Delete event** — Delete an event from a connected Google Calendar account * **Create event** — Create a new event in a connected Google Calendar account ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Google Docs connector > Connect to Google Docs. Create, edit, and collaborate on documents 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Google Docs credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'googledocs' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Google Docs:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'googledocs_list_documents', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "googledocs" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Google Docs:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="googledocs_list_documents", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **List documents** — List all Google Docs documents in the user’s Drive * **Update document** — Update the content of an existing Google Doc using batch update requests * **Read document** — Read the complete content and structure of a Google Doc including text, formatting, tables, and metadata * **Create document** — Create a new blank Google Doc with an optional title ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Google Drive connector > Connect to Google Drive. Manage files, folders, and sharing permissions 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Google Drive credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'googledrive' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Google Drive:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'googledrive_search_files', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "googledrive" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Google Drive:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="googledrive_search_files", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Search files, content** — Search for files and folders in Google Drive using query filters like name, type, owner, and parent folder * **Get file metadata** — Retrieve metadata for a specific file in Google Drive by its file ID ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Google Forms connector > Connect to Google Forms. Create, view, and manage forms and responses securely 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Google Forms credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'googleforms' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Google Forms:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'googleforms_get_form', 25 toolInput: { form_id: 'YOUR_FORM_ID' }, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "googleforms" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Google Forms:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={"form_id":"YOUR_FORM_ID"}, 27 tool_name="googleforms_get_form", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Get response, form** — Get a single response submitted to a Google Form by its response ID * **List responses** — List all responses submitted to a Google Form * **Create form** — Create a new Google Form with a title and optional document title ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Google Meet connector > Connect to Google Meet. Create and manage video meetings with powerful collaboration features 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Google Meet credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'googlemeet' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Google Meet:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first API call through the proxy 21 const result = await actions.request({ 22 connectionName: connector, 23 identifier, 24 path: '/v2/spaces', 25 method: 'GET', 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "googlemeet" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Google Meet:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first API call through the proxy 25 result = actions.request( 26 connection_name=connection_name, 27 identifier=identifier, 28 path="/v2/spaces", 29 method="GET", 30 ) 31 print(result) ``` ## Common workflows [Section titled “Common workflows”](#common-workflows) --- # DOCUMENT BOUNDARY --- # Google Sheets connector > Connect to Google Sheets. Create, edit, and analyze spreadsheets with powerful data management capabilities 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Google Sheets credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'googlesheets' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Google Sheets:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'googlesheets_read_spreadsheet', 25 toolInput: { spreadsheet_id: 'YOUR_SPREADSHEET_ID' }, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "googlesheets" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Google Sheets:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={"spreadsheet_id":"YOUR_SPREADSHEET_ID"}, 27 tool_name="googlesheets_read_spreadsheet", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Values clear, append** — Clear all values in a specified range of a Google Sheets spreadsheet * **Update values** — Update cell values in a specific range of a Google Sheet * **Get values** — Returns only the cell values from a specific range in a Google Sheet — no metadata, no formatting, just the data * **Read spreadsheet** — Returns everything about a spreadsheet — including spreadsheet metadata, sheet properties, cell values, formatting, themes, and pixel sizes * **Create spreadsheet** — Create a new Google Sheets spreadsheet with an optional title and initial sheet configuration ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Google Slides connector > Connect to Google Slides to create, read, and modify presentations programmatically. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Google Slides credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'googleslides' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Google Slides:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'googleslides_read_presentation', 25 toolInput: { presentation_id: 'YOUR_PRESENTATION_ID' }, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "googleslides" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Google Slides:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={"presentation_id":"YOUR_PRESENTATION_ID"}, 27 tool_name="googleslides_read_presentation", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Read presentation** — Read the complete structure and content of a Google Slides presentation including slides, text, images, shapes, and metadata * **Create presentation** — Create a new Google Slides presentation with an optional title ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Granola connector > Connect to Granola to access AI-generated meeting notes, summaries, transcripts, and attendee data from your workspace. Granola automatically records and... 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Make your first call [Section titled “Make your first call”](#make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'granola' 12 const identifier = 'user_123' 13 14 // Make your first call 15 const result = await actions.executeTool({ 16 connector, 17 identifier, 18 toolName: 'granola_notes_list', 19 toolInput: {}, 20 }) 21 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "granola" 14 identifier = "user_123" 15 16 # Make your first call 17 result = actions.execute_tool( 18 tool_input={}, 19 tool_name="granola_notes_list", 20 connection_name=connection_name, 21 identifier=identifier, 22 ) 23 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Get note** — Retrieve a single Granola meeting note by its ID * **List notes** — List all accessible meeting notes in the Granola workspace with pagination and date filtering ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Granola MCP connector > Connect to Granola MCP using OAuth 2.1 with MCP discovery and dynamic client registration. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'granolamcp' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Granola MCP:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'granolamcp_list_meetings', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "granolamcp" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Granola MCP:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="granolamcp_list_meetings", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Get meetings, meeting transcript** — Get detailed meeting information for one or more Granola meetings by ID * **Query granola meetings** — Query Granola about the user’s meetings using natural language * **List meetings** — List the user’s Granola meeting notes within a time range ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # HarvestAPI connector > Connect to HarvestAPI to scrape LinkedIn profiles, companies, and job listings, and search for people and jobs using LinkedIn data. Enables AI agents to... 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your HarvestAPI credentials with Scalekit so it can authenticate requests on your behalf. You do this once per environment. 4. ### Make your first call [Section titled “Make your first call”](#make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'harvestapi' 12 const identifier = 'user_123' 13 14 // Make your first call 15 const result = await actions.executeTool({ 16 connector, 17 identifier, 18 toolName: 'harvestapi_get_ad', 19 toolInput: {}, 20 }) 21 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "harvestapi" 14 identifier = "user_123" 15 16 # Make your first call 17 result = actions.execute_tool( 18 tool_input={}, 19 tool_name="harvestapi_get_ad", 20 connection_name=connection_name, 21 identifier=identifier, 22 ) 23 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Search leads, services, geo** — Search LinkedIn for leads using advanced filters including company, job title, location, seniority, industry, and experience * **Get profile reactions, profile comments, comment reactions** — Retrieve reactions made by a LinkedIn profile * **Profile scrape** — Scrape a LinkedIn profile by URL or public identifier, returning contact details, employment history, education, skills, and more * **Job scrape** — Retrieve full job listing details from LinkedIn by job URL or job ID * **Company scrape** — Scrape a LinkedIn company page for overview, headcount, employee count range, follower count, locations, specialities, industries, and funding data * **Profiles bulk scrape** — Batch scrape multiple LinkedIn profiles in a single request using the HarvestAPI Apify scraper ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # HeyReach connector > Connect to HeyReach to manage LinkedIn outreach campaigns, lead lists, and conversations. List campaigns, retrieve leads, monitor campaign progress, and... 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your HeyReach credentials with Scalekit so it can authenticate requests on your behalf. You do this once per environment. 4. ### Make your first call [Section titled “Make your first call”](#make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'heyreach' 12 const identifier = 'user_123' 13 14 // Make your first call 15 const result = await actions.executeTool({ 16 connector, 17 identifier, 18 toolName: 'heyreach_check_api_key', 19 toolInput: {}, 20 }) 21 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "heyreach" 14 identifier = "user_123" 15 16 # Make your first call 17 result = actions.execute_tool( 18 tool_input={}, 19 tool_name="heyreach_check_api_key", 20 connection_name=connection_name, 21 identifier=identifier, 22 ) 23 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Get lead, conversations, all linkedin accounts** — Retrieve detailed information about a single HeyReach lead by their LinkedIn profile URL * **Campaign add leads to** — Add up to 100 leads to an existing HeyReach campaign * **Key check api** — Verify that your HeyReach API key is valid and the connection is working ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # HubSpot connector > Connect to HubSpot CRM. Manage contacts, deals, companies, and marketing automation 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your HubSpot credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'hubspot' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize HubSpot:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call — list CRM owners 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'hubspot_owners_list', 25 toolInput: {}, 26 }) 27 console.log('HubSpot owners:', result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "hubspot" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize HubSpot:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call — list CRM owners 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="hubspot_owners_list", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print("HubSpot owners:", result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Read CRM records** — retrieve contacts, companies, deals, and tickets * **Create and update records** — add contacts, update deal stages, and log company data * **Log engagements** — record calls, emails, meetings, and notes against any CRM record * **Search and filter** — query CRM objects by property values and associations ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Intercom connector > Connect to Intercom. Send messages, manage conversations, and interact with users and contacts. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Intercom credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'intercom' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Intercom:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first API call through the proxy 21 const result = await actions.request({ 22 connectionName: connector, 23 identifier, 24 path: '/me', 25 method: 'GET', 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "intercom" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Intercom:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first API call through the proxy 25 result = actions.request( 26 connection_name=connection_name, 27 identifier=identifier, 28 path="/me", 29 method="GET", 30 ) 31 print(result) ``` ## Common workflows [Section titled “Common workflows”](#common-workflows) --- # DOCUMENT BOUNDARY --- # Jiminny connector > Connect with Jiminny to access call recordings, transcripts, coaching insights, and conversation intelligence data. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Jiminny credentials with Scalekit so it can authenticate requests on your behalf. You do this once per environment. 4. ### Make your first call [Section titled “Make your first call”](#make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'jiminny' 12 const identifier = 'user_123' 13 14 // Make your first call 15 const result = await actions.executeTool({ 16 connector, 17 identifier, 18 toolName: 'jiminny_activities_list', 19 toolInput: {}, 20 }) 21 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "jiminny" 14 identifier = "user_123" 15 16 # Make your first call 17 result = actions.execute_tool( 18 tool_input={}, 19 tool_name="jiminny_activities_list", 20 connection_name=connection_name, 21 identifier=identifier, 22 ) 23 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Get webhook sample, questions, transcript** — Retrieve a sample webhook payload for a given trigger event type to understand the data structure that will be sent * **Xyz test tool** — Test * **Create webhook** — Create a webhook subscription that sends event payloads to a destination URL when a specified trigger occurs in Jiminny * **List comments, automated call scoring, users** — Retrieve activity comment records with optional filters by user and date range, returning comment IDs, activity IDs, user IDs, and creation timestamps * **Upload activity** — Upload a call or meeting recording file to Jiminny for transcription and analysis, returning the new activity ID on success * **Delete webhook** — Delete an existing webhook subscription by its UUID ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Jira connector > Connect to Jira. Manage issues, projects, workflows, and agile development processes 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Jira credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'jira' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Jira:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'jira_field_search', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "jira" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Jira:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="jira_field_search", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Read issues** — fetch issue details, comments, attachments, and linked items * **Create and update issues** — file bugs, stories, and tasks; update status and assignees * **Manage projects** — list projects, sprints, and boards * **Search with JQL** — execute Jira Query Language searches for advanced filtering ## Common workflows [Section titled “Common workflows”](#common-workflows) **Don’t worry about the Jira cloud ID in the path.** Scalekit resolves `{{cloud_id}}` from the connected account configuration automatically. A request with `path="/rest/api/3/myself"` is routed to the correct Atlassian instance without any extra setup. ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Linear connector > Connect to Linear. Manage issues, projects, sprints, and development workflows 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Linear credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'linear' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Linear:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'linear_issues_list', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "linear" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Linear:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="linear_issues_list", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Read issues** — fetch issues, projects, cycles, and team details * **Create and update issues** — file new issues, update status, set priority, and assign teammates * **Manage projects** — create and update project metadata and milestones * **Search** — find issues by keyword, assignee, label, or state ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # LinkedIn connector > Connect to LinkedIn to manage user authentication, profile data, email, and professional identity via OAuth 2.0 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'linkedin' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize LinkedIn:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'linkedin_ad_accounts_search', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "linkedin" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize LinkedIn:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="linkedin_ad_accounts_search", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Create reaction, organization post, ad account** — Create a reaction (like, praise, empathy, etc.) on a LinkedIn post or comment * **Like post** — Like a LinkedIn post on behalf of a person or organization * **Delete post, campaign, comment** — Delete a UGC post from LinkedIn by its ID * **Update ad account, creative, campaign group** — Partially update a LinkedIn ad account’s name or status * **Search ad accounts, organization, member** — Search LinkedIn ad accounts by status or name * **List posts, post comments, campaign groups** — List posts by a specific author (person or organization URN) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Mailchimp connector > Connect your agent to Mailchimp to manage subscribers, campaigns, lists, and email reports using OAuth 2.0. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Mailchimp credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'mailchimp' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Mailchimp:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'mailchimp_ping', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "mailchimp" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Mailchimp:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="mailchimp_ping", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Manage audiences** — create, update, and delete audiences; list all audiences and their settings * **Manage members** — add, update, upsert, archive, and permanently delete subscribers; get membership and tag details * **Manage segments** — create saved and static segments; list, update, and delete segments; list segment members * **Manage campaigns** — create, update, and delete campaigns; set HTML content; send, schedule, and unschedule campaigns; send test emails * **Manage templates** — create, update, delete, and list custom HTML email templates * **Access reports** — retrieve campaign send reports including opens, clicks, email activity, and unsubscribes * **Manage automations** — list, get, start, and pause classic automation workflows * **Track batch operations** — check the status of asynchronous batch jobs ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Pass the exact tool name from the list below when you call `executeTool` (Node.js) or `execute_tool` (Python). --- # DOCUMENT BOUNDARY --- # Microsoft Excel connector > Connect to Microsoft Excel. Access, read, and modify spreadsheets stored in OneDrive or SharePoint through Microsoft Graph API. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Microsoft Excel credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'microsoftexcel' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Microsoft Excel:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first API call through the proxy 21 const result = await actions.request({ 22 connectionName: connector, 23 identifier, 24 path: '/v1.0/me/drive/root/children', 25 method: 'GET', 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "microsoftexcel" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Microsoft Excel:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first API call through the proxy 25 result = actions.request( 26 connection_name=connection_name, 27 identifier=identifier, 28 path="/v1.0/me/drive/root/children", 29 method="GET", 30 ) 31 print(result) ``` ## Common workflows [Section titled “Common workflows”](#common-workflows) --- # DOCUMENT BOUNDARY --- # Teams connector > Connect to Microsoft Teams. Manage messages, channels, meetings, and team collaboration 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Teams credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'microsoftteams' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Teams:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first API call through the proxy 21 const result = await actions.request({ 22 connectionName: connector, 23 identifier, 24 path: '/v1.0/me', 25 method: 'GET', 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "microsoftteams" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Teams:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first API call through the proxy 25 result = actions.request( 26 connection_name=connection_name, 27 identifier=identifier, 28 path="/v1.0/me", 29 method="GET", 30 ) 31 print(result) ``` ## Common workflows [Section titled “Common workflows”](#common-workflows) --- # DOCUMENT BOUNDARY --- # Microsoft Word connector > Connect to Microsoft Word. Authenticate with your Microsoft account to create, read, and edit Word documents stored in OneDrive or SharePoint through... 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Microsoft Word credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'microsoftword' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Microsoft Word:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first API call through the proxy 21 const result = await actions.request({ 22 connectionName: connector, 23 identifier, 24 path: '/v1.0/me', 25 method: 'GET', 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "microsoftword" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Microsoft Word:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first API call through the proxy 25 result = actions.request( 26 connection_name=connection_name, 27 identifier=identifier, 28 path="/v1.0/me", 29 method="GET", 30 ) 31 print(result) ``` ## Common workflows [Section titled “Common workflows”](#common-workflows) --- # DOCUMENT BOUNDARY --- # Miro connector > Miro is a visual collaboration platform for teams. Manage boards, sticky notes, shapes, cards, frames, connectors, images, and tags using the Miro REST... 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Miro credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'miro' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Miro:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'miro_boards_list', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "miro" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Miro:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="miro_boards_list", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **List board members, tags, mindmap nodes** — Returns a list of members on a Miro board * **Get connector, image, group items** — Retrieves details of a specific connector (line/arrow) on a Miro board * **Create shape, embed, frame** — Creates a shape item on a Miro board * **Remove item tag, board member** — Removes a tag from a specific item on a Miro board * **Invite team member** — Invites a user to a team by email (Enterprise only) * **Delete team, item, sticky note** — Deletes a team from an organization (Enterprise only) ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Monday.com connector > Connect to Monday.com. Manage boards, tasks, workflows, teams, and project collaboration 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Monday.com credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'monday' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Monday:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first API call through the proxy 21 const result = await actions.request({ 22 connectionName: connector, 23 identifier, 24 path: '/v2', 25 method: 'POST', 26 body: JSON.stringify({ query: '{ boards (limit: 5) { id name } }' }), 27 }) 28 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 import json 3 from scalekit.client import ScalekitClient 4 from dotenv import load_dotenv 5 load_dotenv() 6 7 scalekit_client = ScalekitClient( 8 env_url=os.getenv("SCALEKIT_ENV_URL"), 9 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 10 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 11 ) 12 actions = scalekit_client.actions 13 14 connection_name = "monday" 15 identifier = "user_123" 16 17 # Generate an authorization link for the user 18 link_response = actions.get_authorization_link( 19 connection_name=connection_name, 20 identifier=identifier, 21 ) 22 print("Authorize Monday:", link_response.link) 23 input("Press Enter after authorizing...") 24 25 # Make your first API call through the proxy 26 result = actions.request( 27 connection_name=connection_name, 28 identifier=identifier, 29 path="/v2", 30 method="POST", 31 body=json.dumps({"query": "{ boards (limit: 5) { id name } }"}), 32 ) 33 print(result) ``` ## Common workflows [Section titled “Common workflows”](#common-workflows) --- # DOCUMENT BOUNDARY --- # Notion connector > Connect to Notion workspace. Create, edit pages, manage databases, and collaborate on content 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Notion credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'notion' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Notion:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'notion_data_fetch', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "notion" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Notion:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="notion_data_fetch", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Read pages and databases** — retrieve page content and query database entries * **Create pages** — add new pages and database rows with full content * **Update content** — edit existing page blocks, properties, and database fields * **Search** — find pages and databases across the user’s Notion workspace ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # OneDrive connector > Connect to OneDrive. Manage files, folders, and cloud storage with Microsoft OneDrive 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your OneDrive credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'onedrive' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize OneDrive:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first API call through the proxy 21 const result = await actions.request({ 22 connectionName: connector, 23 identifier, 24 path: '/v1.0/me/drive', 25 method: 'GET', 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "onedrive" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize OneDrive:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first API call through the proxy 25 result = actions.request( 26 connection_name=connection_name, 27 identifier=identifier, 28 path="/v1.0/me/drive", 29 method="GET", 30 ) 31 print(result) ``` ## Common workflows [Section titled “Common workflows”](#common-workflows) --- # DOCUMENT BOUNDARY --- # OneNote connector > Connect to Microsoft OneNote. Access, create, and manage notebooks, sections, and pages stored in OneDrive or SharePoint through Microsoft Graph API. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your OneNote credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'onenote' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize OneNote:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first API call through the proxy 21 const result = await actions.request({ 22 connectionName: connector, 23 identifier, 24 path: '/v1.0/me/onenote/notebooks', 25 method: 'GET', 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "onenote" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize OneNote:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first API call through the proxy 25 result = actions.request( 26 connection_name=connection_name, 27 identifier=identifier, 28 path="/v1.0/me/onenote/notebooks", 29 method="GET", 30 ) 31 print(result) ``` ## Common workflows [Section titled “Common workflows”](#common-workflows) --- # DOCUMENT BOUNDARY --- # Outlook connector > Connect to Microsoft Outlook. Manage emails, calendar events, contacts, and tasks 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Outlook credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'outlook' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Outlook:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'outlook_list_calendar_events', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "outlook" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Outlook:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="outlook_list_calendar_events", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Update todo checklist items, todo tasks, todo lists** — Update a checklist item (subtask) in a Microsoft To Do task * **List todo checklist items, todo tasks, todo lists** — List all checklist items (subtasks) for a specific task in a Microsoft To Do task list * **Get todo checklist items, todo tasks, todo lists** — Get a specific checklist item (subtask) from a task in a Microsoft To Do task list * **Delete todo checklist items, todo tasks, todo lists** — Permanently delete a checklist item (subtask) from a task in a Microsoft To Do task list * **Create todo checklist items, todo tasks, todo lists** — Add a checklist item (subtask) to a specific task in a Microsoft To Do task list * **Message reply to** — Reply to an existing email message ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Outreach connector > Connect with Outreach to manage prospects, accounts, sequences, emails, calls, and sales engagement workflows. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'outreach' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Outreach:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'outreach_accounts_list', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "outreach" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Outreach:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="outreach_accounts_list", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Complete tasks** — Mark an existing task as complete in Outreach * **Get sequences, sequence states, webhooks** — Retrieve a single sequence by ID from Outreach * **Delete sequences, opportunities, prospects** — Permanently delete a sequence from Outreach by ID * **Create templates, accounts, tasks** — Create a new email template in Outreach * **List tags, mailboxes, users** — List all tags configured in Outreach that can be applied to prospects, accounts, and sequences * **Update tasks, templates, accounts** — Update an existing task in Outreach ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # PagerDuty connector > Connect to PagerDuty to manage incidents, services, users, teams, escalation policies, schedules, and on-call rotations. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'pagerduty' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize PagerDuty:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'pagerduty_escalation_policies_list', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "pagerduty" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize PagerDuty:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="pagerduty_escalation_policies_list", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **List escalation policies, maintenance windows, schedules** — List escalation policies in PagerDuty * **Create service, incident note, team** — Create a new service in PagerDuty * **Delete user, schedule, escalation policy** — Delete a PagerDuty user * **Update team, incident, maintenance window** — Update an existing PagerDuty team’s name or description * **Get service, maintenance window, escalation policy** — Get details of a specific PagerDuty service by its ID * **Manage incident** — Manage multiple PagerDuty incidents in bulk ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Parallel AI Task MCP connector > Connect to Parallel AI Task MCP to run deep research tasks and task groups directly from your AI workflows. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Parallel AI Task MCP credentials with Scalekit so it can authenticate requests on your behalf. You do this once per environment. 4. ### Make your first call [Section titled “Make your first call”](#make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'parallelaitaskmcp' 12 const identifier = 'user_123' 13 14 // Make your first call 15 const result = await actions.executeTool({ 16 connector, 17 identifier, 18 toolName: 'parallelaitaskmcp_get_result_markdown', 19 toolInput: { taskRunOrGroupId: 'YOUR_TASKRUNORGROUPID' }, 20 }) 21 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "parallelaitaskmcp" 14 identifier = "user_123" 15 16 # Make your first call 17 result = actions.execute_tool( 18 tool_input={"taskRunOrGroupId":"YOUR_TASKRUNORGROUPID"}, 19 tool_name="parallelaitaskmcp_get_result_markdown", 20 connection_name=connection_name, 21 identifier=identifier, 22 ) 23 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Get result markdown, status** — Fetch the final results of a completed Deep Research or Task Group run as Markdown * **Create task group, deep research** — Batch data enrichment tool ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # PhantomBuster connector > Connect to PhantomBuster to automate web scraping and data extraction workflows. Launch, monitor, and manage automation agents that extract data from... 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your PhantomBuster credentials with Scalekit so it can authenticate requests on your behalf. You do this once per environment. 4. ### Make your first call [Section titled “Make your first call”](#make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'phantombuster' 12 const identifier = 'user_123' 13 14 // Make your first call 15 const result = await actions.executeTool({ 16 connector, 17 identifier, 18 toolName: 'phantombuster_org_fetch', 19 toolInput: {}, 20 }) 21 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "phantombuster" 14 identifier = "user_123" 15 16 # Make your first call 17 result = actions.execute_tool( 18 tool_input={}, 19 tool_name="phantombuster_org_fetch", 20 connection_name=connection_name, 21 identifier=identifier, 22 ) 23 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Attach container** — Attach to a running PhantomBuster container and stream its console output in real-time * **Launch agent** — Launch a PhantomBuster automation agent asynchronously * **Fetch agent, org, lists** — Get the output of the most recent container of an agent * **Completions ai** — Get an AI text completion from PhantomBuster’s AI service * **Release branch** — Release (promote to production) specified scripts on a branch in the current PhantomBuster organization * **Stop agent** — Stop a currently running PhantomBuster agent execution ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Pipedrive connector > Connect to Pipedrive CRM. Manage deals, contacts, organizations, activities, leads, and notes to streamline your sales pipeline. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Pipedrive credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'pipedrive' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Pipedrive:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'pipedrive_activities_list', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "pipedrive" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Pipedrive:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="pipedrive_activities_list", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Update product, pipeline, activity** — Update an existing product in Pipedrive * **Get person, deal, lead** — Retrieve details of a specific person (contact) in Pipedrive by their ID, including name, emails, phones, and associated organization * **Me user** — Retrieve the profile of the currently authenticated user in Pipedrive * **Delete webhook, note, organization** — Delete a webhook from Pipedrive by its ID * **List stages, leads, organizations** — Retrieve all stages in Pipedrive * **Create person, product, pipeline** — Create a new person (contact) in Pipedrive with name, email, phone, and optional organization association ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Posthog MCP connector > Connect to Posthog MCP to enable your AI agents and tools to directly interact with PostHog's products. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'posthogmcp' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Posthog MCP:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'posthogmcp_activity_log_list', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "posthogmcp" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Posthog MCP:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="posthogmcp_activity_log_list", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **List workflows, view, subscriptions** — List all workflows in the project * **Get workflows, view, surveys** — Get a specific workflow by ID * **Update view, feature flag, survey** — Update an existing data warehouse saved query (view) * **Unmaterialize view** — Undo materialization for a saved query * **Run view, evaluation** — Get the 5 most recent materialization run statuses for a saved query * **Materialize view** — Enable materialization for a saved query ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # QuickBooks connector > Connect your agent to QuickBooks Online to manage customers, invoices, bills, payments, and financial reports. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your QuickBooks credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'quickbooks' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize QuickBooks:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'quickbooks_company_info_get', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "quickbooks" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize QuickBooks:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="quickbooks_company_info_get", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Manage customers and vendors** — create, update, list, and retrieve customer and vendor records * **Create and manage invoices** — create invoices, update line items, void or delete, and send by email * **Track bills and payments** — create bills from vendors, record bill payments with check or credit card * **Handle estimates and purchase orders** — create, update, and delete estimates; manage purchase orders * **Record journal entries, deposits, and transfers** — post journal entries, create deposits, and transfer funds between accounts * **Manage items and products** — create service and inventory items with pricing and account assignments * **Access financial reports** — retrieve Profit & Loss, Balance Sheet, Cash Flow, Trial Balance, General Ledger, Aged Payables, and Aged Receivables reports * **Manage classes and departments** — organize transactions with classes and departments * **Work with tax codes** — list and retrieve tax codes for accurate tax application ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Pass the exact tool name from the list below when you call `executeTool` (Node.js) or `execute_tool` (Python). --- # DOCUMENT BOUNDARY --- # Salesforce connector > Connect to Salesforce CRM. Manage leads, opportunities, accounts, and customer relationships 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Salesforce credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'salesforce' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Salesforce:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'salesforce_limits_get', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "salesforce" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Salesforce:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="salesforce_limits_get", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Read CRM records** — retrieve accounts, contacts, leads, opportunities, and cases by ID or search query * **Create and update records** — open leads, close opportunities, update deal stages, and edit contacts * **Log activities** — create tasks and events linked to any CRM record * **Run SOQL queries** — execute arbitrary Salesforce Object Query Language queries for custom data retrieval * **Search across objects** — find records by name, email, phone, or any field value * **Call the Metadata API** — use [SOAP proxy calls](#call-the-metadata-api-through-soap-proxy) to inspect and modify Salesforce org metadata ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. ## Call the Metadata API through SOAP proxy [Section titled “Call the Metadata API through SOAP proxy”](#call-the-metadata-api-through-soap-proxy) The [Salesforce Metadata API](https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_intro.htm) is a SOAP-based API for reading and modifying your Salesforce org’s configuration, not its data. Use it to inspect or deploy custom objects, page layouts, validation rules, Apex classes, permission sets, profiles, and other org metadata. Salesforce SOAP APIs only accept opaque access tokens, not JSON Web Token (JWT) access tokens. In your Salesforce Connected App, make sure **Issue JSON Web Token (JWT)-based access tokens for named users** is unchecked. If you disable this option after users have already authenticated, users must re-authenticate before SOAP proxy calls work. ### Get the API version for the connected account The Metadata API SOAP endpoint URL requires a version number. Retrieve the version from the connected account’s `api_config`. ```python 1 import os 2 3 import scalekit.client 4 from dotenv import load_dotenv 5 6 load_dotenv() 7 8 connection_name = "salesforce" # Connection name from the Scalekit dashboard 9 identifier = "6fe1c057-f684-4303-9555-3dd8807319b4" # Your user's identifier as registered in Scalekit 10 11 scalekit_client = scalekit.client.ScalekitClient( 12 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 13 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 14 env_url=os.getenv("SCALEKIT_ENV_URL"), 15 ) 16 actions = scalekit_client.actions 17 18 result = actions.get_connected_account( 19 connection_name=connection_name, 20 identifier=identifier, 21 ) 22 23 raw_version = result.connected_account.api_config.get("version") 24 if not raw_version: 25 raise ValueError("Salesforce connected account is missing api_config.version") 26 27 api_version = raw_version.lstrip("v") # e.g. "66.0" ``` 1. ## Build the SOAP body Construct the SOAP envelope for the operation you want to call. Do not include a `` element. Scalekit injects the session header with the connected account’s access token. The `soap_body` string uses the `api_version` value from the previous section. ```python 1 soap_body = f""" 2 5 6 7 {api_version} 8 9 10 """ ``` 2. ## Send the SOAP request through Scalekit Pass the SOAP body as `raw_body`. Set `Content-Type` to `text/xml; charset=UTF-8` and `SOAPAction` to the operation name. Scalekit resolves the user’s Salesforce instance URL, so the request only needs the Metadata API path. ```python 1 try: 2 response = actions.request( 3 connection_name=connection_name, 4 identifier=identifier, 5 path=f"/services/Soap/m/{api_version}", 6 method="POST", 7 raw_body=soap_body, 8 headers={ 9 "Content-Type": "text/xml; charset=UTF-8", 10 "SOAPAction": "describeMetadata", 11 }, 12 ) 13 except Exception as exc: 14 raise RuntimeError("Salesforce Metadata API SOAP proxy request failed") from exc 15 16 print(response.content) ``` --- # DOCUMENT BOUNDARY --- # ServiceNow connector > Connect to ServiceNow. Manage incidents, service requests, CMDB, and IT service management workflows 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your ServiceNow credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'servicenow' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize ServiceNow:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first API call through the proxy 21 const result = await actions.request({ 22 connectionName: connector, 23 identifier, 24 path: '/api/now/table/sys_user', 25 method: 'GET', 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "servicenow" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize ServiceNow:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first API call through the proxy 25 result = actions.request( 26 connection_name=connection_name, 27 identifier=identifier, 28 path="/api/now/table/sys_user", 29 method="GET", 30 ) 31 print(result) ``` ## Common workflows [Section titled “Common workflows”](#common-workflows) **Don’t worry about your ServiceNow instance domain in the path.** Scalekit automatically resolves `{{domain}}` from the connected account’s configuration. For example, a request with `path="/api/now/table/sys_user"` will be sent to `https://mycompany.service-now.com/api/now/table/sys_user` automatically. --- # DOCUMENT BOUNDARY --- # SharePoint connector > Connect to SharePoint. Manage sites, documents, lists, and collaborative content 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your SharePoint credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'sharepoint' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize SharePoint:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first API call through the proxy 21 const result = await actions.request({ 22 connectionName: connector, 23 identifier, 24 path: '/v1.0/me/sites', 25 method: 'GET', 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "sharepoint" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize SharePoint:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first API call through the proxy 25 result = actions.request( 26 connection_name=connection_name, 27 identifier=identifier, 28 path="/v1.0/me/sites", 29 method="GET", 30 ) 31 print(result) ``` ## Common workflows [Section titled “Common workflows”](#common-workflows) --- # DOCUMENT BOUNDARY --- # Slack connector > Connect to Slack workspace. Send Messages as Bots or on behalf of users 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Slack credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'slack' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Slack:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'slack_list_channels', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "slack" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Slack:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="slack_list_channels", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Send messages** — post to channels, DMs, and threads on behalf of your users * **Read conversations** — retrieve channel history, thread replies, and direct messages * **Manage channels** — create channels, invite members, and update channel settings * **Look up users** — search for team members by name, email, or username * **Upload files** — share files and attachments into any conversation ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Snowflake connector > Connect to Snowflake to manage and analyze your data warehouse workloads 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Snowflake credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'snowflake' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Snowflake:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'snowflake_cancel_query', 25 toolInput: { statement_handle: 'YOUR_STATEMENT_HANDLE' }, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "snowflake" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Snowflake:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={"statement_handle":"YOUR_STATEMENT_HANDLE"}, 27 tool_name="snowflake_cancel_query", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Grants show** — Run SHOW GRANTS in common modes (to role, to user, of role, on object) * **Warehouses show** — Run SHOW WAREHOUSES * **Schemas show databases** — Run SHOW DATABASES or SHOW SCHEMAS * **Keys show imported exported, show primary** — Run SHOW IMPORTED KEYS or SHOW EXPORTED KEYS for a table * **Get referential constraints, table constraints, schemata** — Query INFORMATION\_SCHEMA.REFERENTIAL\_CONSTRAINTS * **Query cancel** — Cancel a running Snowflake SQL API statement by statement handle ## Common workflows [Section titled “Common workflows”](#common-workflows) **Don’t worry about your Snowflake account domain in the path.** Scalekit automatically resolves `{{domain}}` from the connected account’s configuration. For example, a request with `path="/api/v2/statements"` will be sent to `https://myorg-myaccount.snowflakecomputing.com/api/v2/statements` automatically. ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Snowflake Key Pair Auth connector > Connect to Snowflake via Public Private Key Pair to manage and analyze your data warehouse workloads 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Make your first call [Section titled “Make your first call”](#make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'snowflakekeyauth' 12 const identifier = 'user_123' 13 14 // Make your first call 15 const result = await actions.executeTool({ 16 connector, 17 identifier, 18 toolName: 'snowflakekeyauth_cancel_query', 19 toolInput: { statement_handle: 'YOUR_STATEMENT_HANDLE' }, 20 }) 21 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "snowflakekeyauth" 14 identifier = "user_123" 15 16 # Make your first call 17 result = actions.execute_tool( 18 tool_input={"statement_handle":"YOUR_STATEMENT_HANDLE"}, 19 tool_name="snowflakekeyauth_cancel_query", 20 connection_name=connection_name, 21 identifier=identifier, 22 ) 23 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Warehouses show** — Run SHOW WAREHOUSES * **Keys show primary, show imported exported** — Run SHOW PRIMARY KEYS with optional scope * **Grants show** — Run SHOW GRANTS in common modes (to role, to user, of role, on object) * **Schemas show databases** — Run SHOW DATABASES or SHOW SCHEMAS * **Get tables, table constraints, schemata** — Query INFORMATION\_SCHEMA.TABLES for table metadata in a Snowflake database * **Query cancel** — Cancel a running Snowflake SQL API statement by statement handle ## Common workflows [Section titled “Common workflows”](#common-workflows) **Don’t worry about your Snowflake account domain in the path.** Scalekit automatically resolves `{{domain}}` from the connected account’s configuration. For example, a request with `path="/api/v2/statements"` will be sent to `https://myorg-myaccount.snowflakecomputing.com/api/v2/statements` automatically. ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Supadata connector > Connect with Supadata to extract transcripts, metadata, and structured content from YouTube, social media, and the web using AI. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Supadata credentials with Scalekit so it can authenticate requests on your behalf. You do this once per environment. 4. ### Make your first call [Section titled “Make your first call”](#make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'supadata' 12 const identifier = 'user_123' 13 14 // Make your first call 15 const result = await actions.executeTool({ 16 connector, 17 identifier, 18 toolName: 'supadata_metadata_get', 19 toolInput: { url: 'https://example.com/url' }, 20 }) 21 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "supadata" 14 identifier = "user_123" 15 16 # Make your first call 17 result = actions.execute_tool( 18 tool_input={"url":"https://example.com/url"}, 19 tool_name="supadata_metadata_get", 20 connection_name=connection_name, 21 identifier=identifier, 22 ) 23 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Get metadata, youtube playlist, youtube channel** — Retrieve unified metadata for a video or media URL including title, description, author info, engagement stats, media details, and creation date * **Scrape web** — Scrape a web page and return its content as clean Markdown * **Search youtube** — Search YouTube for videos, channels, or playlists * **Map web** — Discover and return all URLs found on a website * **Translate youtube transcript** — Retrieve and translate a YouTube video transcript into a target language ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Tableau connector > Connect to Tableau Cloud or Tableau Server to browse workbooks, views, and data sources, export visualizations, and query underlying data. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Tableau credentials with Scalekit so it can authenticate requests on your behalf. You do this once per environment. 4. ### Make your first call [Section titled “Make your first call”](#make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'tableau' 12 const identifier = 'user_123' 13 14 // Make your first call 15 const result = await actions.executeTool({ 16 connector, 17 identifier, 18 toolName: 'tableau_datasources_list', 19 toolInput: {}, 20 }) 21 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "tableau" 14 identifier = "user_123" 15 16 # Make your first call 17 result = actions.execute_tool( 18 tool_input={}, 19 tool_name="tableau_datasources_list", 20 connection_name=connection_name, 21 identifier=identifier, 22 ) 23 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **List workbooks, workbook connections, views** — Retrieve a filtered, sorted list of workbooks on a specified Tableau site * **Search workbook** — Search for workbooks on a Tableau site by name * **Get workbook, view, user** — Retrieve detailed information about a specific Tableau workbook by its ID, including metadata, project, owner, tags, and optional usage statistics * **Delete workbook, project, datasource** — Delete a workbook from a Tableau site * **Site user remove from, user add to** — Remove a user from a Tableau site * **Query view** — Run a structured query against a published Tableau data source using the VizQL Data Service API ## Common workflows [Section titled “Common workflows”](#common-workflows) The **site ID** (site LUID) is resolved automatically from the connected account after sign-in. You do not pass `site_id` to tool calls. For proxy API calls that require a site ID in the URL path, call `tableau_session_get` once to retrieve it. ## Getting resource IDs [Section titled “Getting resource IDs”](#getting-resource-ids) Most Tableau tools require one or more resource LUIDs. The **site ID is resolved automatically** by Scalekit after sign-in — you do not pass it to tool calls. Always fetch other IDs from the API — never guess or hard-code them. | Resource | Tool to get ID | Field in response | | -------------------- | ----------------------------------------------------- | ----------------------------- | | Workbook ID | `tableau_workbooks_list` or `tableau_workbook_search` | `workbooks.workbook[].id` | | View ID | `tableau_views_list` or `tableau_workbook_views_list` | `views.view[].id` | | Data Source ID | `tableau_datasources_list` | `datasources.datasource[].id` | | Project ID | `tableau_projects_list` | `projects.project[].id` | | User ID | `tableau_users_list` | `users.user[].id` | | Group ID | `tableau_groups_list` | `groups.group[].id` | | Job ID | `tableau_job_get` (from background job operations) | `job.id` | | Site ID (proxy only) | `tableau_session_get` | `session.site.id` | **Recommended start sequence for any agent session:** ```text 1 1. tableau_workbooks_list → discover workbooks 2 2. tableau_workbook_views_list → discover views within a workbook 3 3. tableau_datasources_list → discover data sources ``` ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Trello connector > Connect to Trello. Manage boards, cards, lists, and team collaboration workflows 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'trello' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Trello:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first API call through the proxy 21 const result = await actions.request({ 22 connectionName: connector, 23 identifier, 24 path: '/1/members/me', 25 method: 'GET', 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "trello" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Trello:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first API call through the proxy 25 result = actions.request( 26 connection_name=connection_name, 27 identifier=identifier, 28 path="/1/members/me", 29 method="GET", 30 ) 31 print(result) ``` ## Common workflows [Section titled “Common workflows”](#common-workflows) --- # DOCUMENT BOUNDARY --- # Twitter / X connector > Connect to Twitter. Read and write Tweets, read users, manage follows, bookmarks, etc. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Twitter / X credentials with Scalekit so it can authenticate requests on your behalf. You do this once per environment. 4. ### Make your first call [Section titled “Make your first call”](#make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'twitter' 12 const identifier = 'user_123' 13 14 // Make your first call 15 const result = await actions.executeTool({ 16 connector, 17 identifier, 18 toolName: 'twitter_dm_events_get', 19 toolInput: {}, 20 }) 21 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "twitter" 14 identifier = "user_123" 15 16 # Make your first call 17 result = actions.execute_tool( 18 tool_input={}, 19 tool_name="twitter_dm_events_get", 20 connection_name=connection_name, 21 identifier=identifier, 22 ) 23 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Get media upload status, post likers, user followed lists** — Gets the status of a media upload for X/Twitter * **Lookup users, posts, user** — Retrieves detailed information for specified X (formerly Twitter) user IDs * **Unmute user** — Unmutes a target user for the authenticated user, allowing them to see Tweets and notifications from the target user again * **List delete, member remove, follow** — Permanently deletes a specified Twitter List using its ID * **Search full archive, recent** — Searches the full archive of public Tweets from March 2006 onwards * **Upload media** — Uploads media (images only) to X/Twitter using the v2 API ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Vercel connector > Connect to Vercel. Access user profile, teams, projects, deployments, and environment settings. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Vercel credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'vercel' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Vercel:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'vercel_aliases_list', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "vercel" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Vercel:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="vercel_aliases_list", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Create env var, edge config, project** — Creates a new environment variable for a Vercel project with the specified key, value, and target environments * **Add domain, project domain** — Adds a domain to the authenticated user or team’s Vercel account * **Delete team, deployment, alias** — Permanently deletes a Vercel team and all its associated resources * **List domains, team members, deployments** — Returns all domains registered or added to the authenticated user or team’s Vercel account * **Get team, user, alias** — Returns details of a specific Vercel team by its ID or slug * **Update edge config items, env var, project** — Creates, updates, or deletes items in an Edge Config store using a list of patch operations ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Vimeo connector > Connect to Vimeo API v3.4. Upload and manage videos, organize content into showcases and folders, manage channels, handle comments, likes, and webhooks. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Vimeo credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'vimeo' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Vimeo:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'vimeo_categories_list', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "vimeo" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Vimeo:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="vimeo_categories_list", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **List watchlater, showcase videos, following** — Retrieve all videos in the authenticated user’s Vimeo Watch Later queue * **Add showcase video, folder video, watchlater** — Add a video to a Vimeo showcase * **Follow user** — Follow a Vimeo user on behalf of the authenticated user * **Create folder, showcase, webhook** — Create a new folder (project) in the authenticated user’s Vimeo account for organizing private video content * **Delete video, webhook** — Permanently delete a Vimeo video * **Get video, me, user** — Retrieve detailed information about a specific Vimeo video including metadata, privacy settings, stats, and embed details ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Xero connector > Connect to Xero to manage invoices, contacts, payments, accounts, and financial reports via OAuth 2.0. 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Xero credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'xero' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Xero:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'xero_accounts_list', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "xero" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Xero:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="xero_accounts_list", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Manage the chart of accounts** — list, create, update, and archive accounts * **Work with contacts** — create and update customers and suppliers, manage contact groups * **Create and manage invoices** — draft, authorise, update, and void invoices and bills * **Handle payments and credit notes** — list payments, overpayments, prepayments, batch payments, and credit notes * **Manage inventory** — create, update, and delete inventory items * **Process purchase orders and quotes** — create, update, and track purchase orders and quotes * **Record manual journals** — create and post manual journal entries * **Manage employees** — create and update employee records * **Run financial reports** — generate Balance Sheet, Profit & Loss, Trial Balance, Aged Payables/Receivables, Bank Summary, and Executive Summary reports * **Access organisation settings** — list currencies, tax rates, tracking categories, and users ## Common workflows [Section titled “Common workflows”](#common-workflows) Contact field must be a JSON string The `Contact` parameter in `xero_invoice_create`, `xero_credit_note_create`, `xero_purchase_order_create`, and `xero_quote_create` must be passed as a JSON **string**, not an object: `'{"ContactID": "abc123..."}'`. Pass the result of `JSON.stringify({ContactID: id})` in Node.js or `json.dumps({"ContactID": id})` in Python. ## Common patterns [Section titled “Common patterns”](#common-patterns) ### Void (delete) an invoice `xero_invoice_delete` voids an invoice by setting its status to `VOIDED`. Xero only allows voiding invoices that are in `AUTHORISED` status — calling it on a `DRAFT` invoice returns a validation error. The correct sequence is: 1. Authorise the invoice with `xero_invoice_update`, passing `Status: "AUTHORISED"` and a `DueDate`. 2. Call `xero_invoice_delete` with the same `invoice_id`. ### Pass Contact and LineItems correctly Several tools (`xero_invoice_create`, `xero_credit_note_create`, `xero_purchase_order_create`, `xero_quote_create`) take a `Contact` field and a `LineItems` field. * `Contact` — pass as a **JSON string**: `'{"ContactID": "abc123..."}'` * `LineItems` — pass as a **JSON array** (not a string): `[{"Description": "...", "Quantity": 1, "UnitAmount": 100, "AccountCode": "200"}]` Include `AccountCode` in each line item whenever the invoice may later be authorised or voided. ### Quotes require a Date `xero_quote_create` and `xero_quote_update` both require a `Date` field (ISO 8601, e.g. `"2026-04-29"`). Xero returns a validation error `"Date cannot be empty"` without it. `xero_quote_update` also requires `Contact` (JSON string) in addition to `Date`. ### Aged reports require a contactID `xero_report_aged_payables` and `xero_report_aged_receivables` require a `contactID` parameter. The other five report tools (`xero_report_balance_sheet`, `xero_report_profit_and_loss`, `xero_report_trial_balance`, `xero_report_bank_summary`, `xero_report_executive_summary`) require no inputs beyond the auto-injected tenant ID. ### Update an item `xero_item_update` requires `Code` in the request body (in addition to `item_id` in the path). Pass the item’s existing code or a new one — Xero uses it to identify the item being updated. ## Tool list [Section titled “Tool list”](#tool-list) Pass the exact tool name from the list below when you call `executeTool` (Node.js) or `execute_tool` (Python). --- # DOCUMENT BOUNDARY --- # YouTube connector > Connect to YouTube to access channel details, analytics, and upload or manage videos via OAuth 2.0 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your YouTube credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'youtube' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize YouTube:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first call 21 const result = await actions.executeTool({ 22 connector, 23 identifier, 24 toolName: 'youtube_analytics_groups_list', 25 toolInput: {}, 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "youtube" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize YouTube:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first call 25 result = actions.execute_tool( 26 tool_input={}, 27 tool_name="youtube_analytics_groups_list", 28 connection_name=connection_name, 29 identifier=identifier, 30 ) 31 print(result) ``` ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Search records** — Search for videos, channels, and playlists on YouTube * **List reporting, analytics groups** — List reports that have been generated for a YouTube reporting job * **Query analytics** — Query YouTube Analytics data to retrieve metrics like views, watch time, subscribers, revenue, etc * **Update videos, analytics groups, playlist** — Update metadata for an existing YouTube video * **Delete subscriptions, reporting jobs, analytics groups** — Unsubscribe the authenticated user from a YouTube channel using the subscription ID * **Insert playlist, playlist items, analytics group item** — Create a new YouTube playlist for the authenticated user ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Zendesk connector > Connect to Zendesk. Manage customer support tickets, users, organizations, and help desk operations 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Zendesk credentials with Scalekit so it can authenticate requests on your behalf. You do this once per environment. ## What you can do [Section titled “What you can do”](#what-you-can-do) Connect this agent connector to let your agent: * **Get side conversation, user, ticket** — Retrieve a specific side conversation on a Zendesk ticket by its ID * **List side conversations, tickets, views** — List all side conversations on a Zendesk ticket * **Update ticket** — Update an existing Zendesk ticket * **Reply ticket** — Add a public reply or internal note to a Zendesk ticket * **Search tickets** — Search Zendesk tickets using a query string * **Create user, ticket** — Create a new user in Zendesk ## Common workflows [Section titled “Common workflows”](#common-workflows) ## Tool list [Section titled “Tool list”](#tool-list) Use the exact tool names from the **Tool list** below when you call `execute_tool`. If you’re not sure which name to use, list the tools available for the current user first. --- # DOCUMENT BOUNDARY --- # Zoom connector > Connect to Zoom. Schedule meetings, manage recordings, and handle video conferencing workflows 1. ### Install the SDK [Section titled “Install the SDK”](#install-the-sdk) * Node.js ```bash 1 npm install @scalekit-sdk/node ``` * Python ```bash 1 pip install scalekit ``` Full SDK reference: [Node.js](/agentkit/sdks/node/) | [Python](/agentkit/sdks/python/) 2. ### Set your credentials [Section titled “Set your credentials”](#set-your-credentials) Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**. .env ```sh SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET= ``` 3. ### Set up the connector [Section titled “Set up the connector”](#set-up-the-connector) Register your Zoom credentials with Scalekit so it handles the token lifecycle. You do this once per environment. 4. ### Authorize and make your first call [Section titled “Authorize and make your first call”](#authorize-and-make-your-first-call) * Node.js quickstart.ts ```typescript 1 import { ScalekitClient } from '@scalekit-sdk/node' 2 import 'dotenv/config' 3 4 const scalekit = new ScalekitClient( 5 process.env.SCALEKIT_ENV_URL, 6 process.env.SCALEKIT_CLIENT_ID, 7 process.env.SCALEKIT_CLIENT_SECRET, 8 ) 9 const actions = scalekit.actions 10 11 const connector = 'zoom' 12 const identifier = 'user_123' 13 14 // Generate an authorization link for the user 15 const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier }) 16 console.log('Authorize Zoom:', link) 17 process.stdout.write('Press Enter after authorizing...') 18 await new Promise(r => process.stdin.once('data', r)) 19 20 // Make your first API call through the proxy 21 const result = await actions.request({ 22 connectionName: connector, 23 identifier, 24 path: '/v2/users/me', 25 method: 'GET', 26 }) 27 console.log(result) ``` * Python quickstart.py ```python 1 import os 2 from scalekit.client import ScalekitClient 3 from dotenv import load_dotenv 4 load_dotenv() 5 6 scalekit_client = ScalekitClient( 7 env_url=os.getenv("SCALEKIT_ENV_URL"), 8 client_id=os.getenv("SCALEKIT_CLIENT_ID"), 9 client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), 10 ) 11 actions = scalekit_client.actions 12 13 connection_name = "zoom" 14 identifier = "user_123" 15 16 # Generate an authorization link for the user 17 link_response = actions.get_authorization_link( 18 connection_name=connection_name, 19 identifier=identifier, 20 ) 21 print("Authorize Zoom:", link_response.link) 22 input("Press Enter after authorizing...") 23 24 # Make your first API call through the proxy 25 result = actions.request( 26 connection_name=connection_name, 27 identifier=identifier, 28 path="/v2/users/me", 29 method="GET", 30 ) 31 print(result) ``` ## Common workflows [Section titled “Common workflows”](#common-workflows)