A unified interface for performing actions on SaaS tools through AI-friendly APIs.
StackOne provides two toolsets:
OpenAPIToolSet: A toolset generated from supplied OpenAPI specificationsStackOneToolSet: A toolset preloaded with StackOne Unified Tools (every StackOne Unified API endpoints has its tool equivalent in this toolset)
These toolsets provide functionality to filter, transform, and execute tools. The toolsets can be easily used via common AI Agent libraries.
Under the hood the StackOneToolSet uses the same OpenAPIParser as the OpenAPIToolSet, but provides some convenience methods for using StackOne API keys and account IDs.
# Using npm
npm install @stackone/ai
# Using yarn
yarn add @stackone/ai
# Using bun
bun add @stackone/aiIf you plan to use the AI SDK integration (Vercel AI SDK), install it separately:
# Using npm
npm install ai
# Using yarn
yarn add ai
# Using bun
bun add aiThe OpenAPIToolSet and StackOneToolSet make it super easy to use these APIs as tools in your AI applications.
import { OpenAI } from "openai";
import { StackOneToolSet } from "@stackone/ai";
const toolset = new StackOneToolSet();
const tools = toolset.getTools("hris_*", "<stackone-account-id>").toOpenAI();
await openai.chat.completions.create({
model: "gpt-5",
messages: [
{
role: "system",
content: "You are a helpful HR assistant.",
},
{
role: "user",
content: "Create a time-off request for employee id cxIQ5764hj2",
},
],
tools: tools,
});import { openai } from "@ai-sdk/openai";
import { generateText } from "ai";
import { StackOneToolSet } from "@stackone/ai";
const toolset = new StackOneToolSet();
const aiSdkTools = await toolset.getTools("hris_*").toAISDK();
await generateText({
model: openai("gpt-5"),
tools: aiSdkTools,
maxSteps: 3,
});The StackOneToolSet is an extension of the OpenAPIToolSet that adds some convenience methods for using StackOne API keys and account IDs and some other features.
import { StackOneToolSet } from "@stackone/ai";
const toolset = new StackOneToolSet();
const tools = toolset.getTools("hris_*", "your-account-id");
const employeeTool = tools.getTool("hris_list_employees");
const employees = await employeeTool.execute();Set the STACKONE_API_KEY environment variable:
export STACKONE_API_KEY=<your-api-key>or load from a .env file using your preferred environment variable library.
StackOne uses account IDs to identify different integrations. You can specify the account ID at different levels:
import { StackOneToolSet } from "@stackone/ai";
// Method 1: Set at toolset initialization
const toolset = new StackOneToolSet({ accountId: "your-account-id" });
// Method 2: Set when getting tools (overrides toolset account ID)
const tools = toolset.getTools("hris_*", "override-account-id");
// Method 3: Set directly on a tool instance
tools.setAccountId("direct-account-id");
const currentAccountId = tools.getAccountId(); // Get the current account IDCall fetchTools() when you want the SDK to pull the current tool definitions directly from StackOne without maintaining local specs:
const toolset = new StackOneToolSet({
baseUrl: "https://api.stackone.com",
});
const tools = await toolset.fetchTools();
const employeeTool = tools.getTool("hris_list_employees");
const result = await employeeTool?.execute({
query: { limit: 5 },
});fetchTools() reuses the credentials you already configured (for example via STACKONE_API_KEY) and binds the returned tool objects to StackOne's actions client.
You can filter tools by account IDs, providers, and action patterns:
// Filter by account IDs
toolset.setAccounts(["account-123", "account-456"]);
const tools = await toolset.fetchTools();
// OR
const tools = await toolset.fetchTools({
accountIds: ["account-123", "account-456"],
});
// Filter by providers
const tools = await toolset.fetchTools({ providers: ["hibob", "bamboohr"] });
// Filter by actions with exact match
const tools = await toolset.fetchTools({
actions: ["hibob_list_employees", "hibob_create_employees"],
});
// Filter by actions with glob patterns
const tools = await toolset.fetchTools({ actions: ["*_list_employees"] });
// Combine multiple filters
const tools = await toolset.fetchTools({
accountIds: ["account-123"],
providers: ["hibob"],
actions: ["*_list_*"],
});This is especially useful when you want to:
- Limit tools to specific linked accounts
- Focus on specific HR/CRM/ATS providers
- Get only certain types of operations (e.g., all "list" operations)
The StackOneToolSet comes with built-in transformations for file uploads:
import { StackOneToolSet } from "@stackone/ai";
const toolset = new StackOneToolSet();
const tools = toolset.getTools("*file_upload*");
const fileUploadTool = tools.getTool("storage_file_upload");
// Execute with just the file_path parameter
// The file_content, file_name, and file_format will be derived automatically
const result = await fileUploadTool.execute({ file_path: "/path/to/file.pdf" });Note: you can build your own custom transformations using both toolset classes. See the Parameter Transformations section for more information.
The OpenAPIToolSet class allows you to parse OpenAPI specifications as tools from either a local file or a remote URL.
import { OpenAPIToolSet } from "@stackone/ai";
import path from "node:path";
// Create the toolset
const toolset = new OpenAPIToolSet({
filePath: path.join(import.meta.dirname, "path/to/openapi-spec.json");
});
// Get all tools
const allTools = toolset.getTools();
// Get filtered tools
const filteredTools = toolset.getTools("user_*");import { OpenAPIToolSet } from "@stackone/ai";
// Create the toolset using the factory method
const toolset = await OpenAPIToolSet.fromUrl({
url: "https://example.com/path/to/openapi-spec.json",
});The OpenAPIToolSet supports easy usage of bot Basic and Bearer authentication:
// Basic Authentication
const toolsetWithBasicAuth = new OpenAPIToolSet({
filePath: "path/to/spec.json",
authentication: {
type: "basic",
credentials: {
username: "user",
password: "pass",
},
},
});
// Bearer Authentication
const toolsetWithBearerAuth = await OpenAPIToolSet.fromUrl({
url: "https://example.com/spec.json",
authentication: {
type: "bearer",
credentials: {
token: "your-bearer-token",
},
},
});You can also directly write to the toolset headers:
const toolsetWithHeaders = new OpenAPIToolSet({
filePath: "path/to/spec.json",
headers: {
Authorization: "Bearer your-bearer-token",
},
});These are some of the features which you can use with the OpenAPIToolSet and StackOneToolSet.
The StackOne AI SDK includes a built-in feedback collection tool (meta_collect_tool_feedback) that allows users to provide feedback on their experience with StackOne tools. This tool is automatically included in the StackOneToolSet and helps improve the SDK based on user input.
The feedback tool:
- Requires explicit user consent before submitting feedback
- Collects user feedback about their experience with StackOne tools
- Tracks tool usage by recording which tools were used
- Submits to StackOne via the
/ai/tool-feedbackendpoint - Uses the same API key as other SDK operations for authentication
The feedback tool is automatically available when using StackOneToolSet:
import { StackOneToolSet } from "@stackone/ai";
const toolset = new StackOneToolSet();
const tools = toolset.getTools("*", "account-id");
// The feedback tool is automatically included
const feedbackTool = tools.getTool("meta_collect_tool_feedback");
// Use with AI agents - they will ask for user consent first
const openAITools = tools.toOpenAI();
// or
const aiSdkTools = await tools.toAISDK();You can also use the feedback tool directly:
// Get the feedback tool
const feedbackTool = tools.getTool("meta_collect_tool_feedback");
// Submit feedback (after getting user consent)
const result = await feedbackTool.execute({
feedback: "The tools worked great! Very easy to use.",
account_id: "acc_123456",
tool_names: ["hris_list_employees", "hris_create_time_off"],
});The feedback tool supports both single and multiple account IDs. When you provide an array of account IDs, the feedback will be sent to each account individually:
// Single account ID (string)
await feedbackTool.execute({
feedback: "The tools worked great! Very easy to use.",
account_id: "acc_123456",
tool_names: ["hris_list_employees", "hris_create_time_off"],
});
// Multiple account IDs (array)
await feedbackTool.execute({
feedback: "The tools worked great! Very easy to use.",
account_id: ["acc_123456", "acc_789012"],
tool_names: ["hris_list_employees", "hris_create_time_off"],
});Response Format: When using multiple account IDs, the tool returns a summary of all submissions:
{
message: "Feedback sent to 2 account(s)",
total_accounts: 2,
successful: 2,
failed: 0,
results: [
{
account_id: "acc_123456",
status: "success",
result: { message: "Feedback successfully stored", ... }
},
{
account_id: "acc_789012",
status: "success",
result: { message: "Feedback successfully stored", ... }
}
]
}When AI agents use this tool, they will:
- Ask for user consent: "Are you ok with sending feedback to StackOne?"
- Collect feedback: Get the user's verbatim feedback
- Track tool usage: Record which tools were used in the session
- Submit to all accounts: Send the same feedback to each account ID provided
- Report results: Show which accounts received the feedback successfully
The tool description includes clear instructions for AI agents to always ask for explicit user consent before submitting feedback.
Meta tools enable dynamic tool discovery and execution, allowing AI agents to search for relevant tools based on natural language queries without hardcoding tool names.
⚠️ Beta Feature: Meta tools are currently in beta and the API may change in future versions.
Meta tools provide two core capabilities:
- Tool Discovery (
meta_search_tools): Search for tools using natural language queries - Tool Execution (
meta_execute_tool): Execute discovered tools dynamically
The tool discovery uses Orama's BM25 algorithm for relevance ranking, providing high-quality search results based on tool names, descriptions, and categories.
import { StackOneToolSet } from "@stackone/ai";
const toolset = new StackOneToolSet();
const tools = toolset.getStackOneTools("*", "account-id");
// Get meta tools for dynamic discovery
const metaTools = await tools.metaTools();
// Use with OpenAI
const openAITools = metaTools.toOpenAI();
// Use with AI SDK
const aiSdkTools = await metaTools.toAISDK();import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
const { text } = await generateText({
model: openai("gpt-4o-mini"),
tools: aiSdkTools,
prompt: "Find tools for managing employees and create a time off request",
maxSteps: 3, // Allow multiple tool calls
});// Step 1: Discover relevant tools
const filterTool = metaTools.getTool("meta_search_tools");
const searchResult = await filterTool.execute({
query: "employee time off vacation",
limit: 5,
minScore: 0.3, // Minimum relevance score (0-1)
});
// Step 2: Execute a discovered tool
const executeTool = metaTools.getTool("meta_execute_tool");
const result = await executeTool.execute({
toolName: "hris_create_time_off",
params: {
employeeId: "emp_123",
startDate: "2024-01-15",
endDate: "2024-01-19",
},
});import { StackOneToolSet } from "@stackone/ai";
const toolset = new StackOneToolSet({ baseUrl: "https://api.example-dev.com" });This two-stage transformation approach allows you to:
- Schema Override: Change the tool's input schema at creation time
- PreExecute: Transform parameters from the override schema back to the original API format at execution time
This is particularly powerful for document handling, where you can simplify complex file upload parameters:
import { StackOneToolSet } from "@stackone/ai";
import type {
Experimental_SchemaOverride,
Experimental_PreExecuteFunction,
} from "@stackone/ai";
// 1. Schema Override: Simplify the input schema
const documentSchemaOverride: Experimental_SchemaOverride = (
originalSchema
) => {
// Replace complex file parameters with simple doc_id
const newProperties = { ...originalSchema.properties };
delete newProperties.content;
delete newProperties.name;
delete newProperties.file_format;
newProperties.doc_id = {
type: "string",
description: "Document path or identifier",
};
return { ...originalSchema, properties: newProperties };
};
// 2. PreExecute: Transform the simplified parameters back to API format
const documentPreExecute: Experimental_PreExecuteFunction = async (params) => {
const { doc_id, ...otherParams } = params;
// Read file and convert to required API format
const fileContent = readFileSync(doc_id);
const base64Content = fileContent.toString("base64");
const fileName = basename(doc_id);
const extension = extname(doc_id).slice(1);
return {
...otherParams,
content: base64Content,
name: fileName,
file_format: { value: extension },
};
};
// Use the experimental transformation
const toolset = new StackOneToolSet();
const tools = toolset.getStackOneTools("hris_*", "account_id");
const documentTool = tools.getTool("hris_upload_employee_document", {
experimental_schemaOverride: documentSchemaOverride,
experimental_preExecute: documentPreExecute,
});
// Now you can use the simplified schema
const result = await documentTool.execute({
doc_id: "/path/to/document.pdf", // Simplified input
id: "employee_123",
category: { value: "shared" },
});You can use the dryRun option to return the api arguments from a tool call without making the actual api call:
import { StackOneToolSet } from "stackone-ai-node";
import assert from "node:assert";
// Initialize the toolset
const toolset = new StackOneToolSet();
const fileUploadTool = toolset
.getTools("*file_upload*")
.getTool("storage_file_upload");
// Use dryRun to see how the file path is derived into other parameters
const dryRunResult = await fileUploadTool.execute(
{ file_path: "/path/to/file.pdf" },
{ dryRun: true }
);
// Verify the derived parameters
assert("file_content" in dryRunResult.mappedParams);
assert("file_name" in dryRunResult.mappedParams);
assert("file_format" in dryRunResult.mappedParams);The dryRun option returns an object containing:
url: The full URL with query parametersmethod: The HTTP methodheaders: The request headersbody: The request body (or '[FormData]' for multipart form data)mappedParams: The parameters after mapping and derivationoriginalParams: The original parameters provided to the execute method