Skip to content

Connect Torii to AI Agents: Automate SaaS Lifecycles and App Data

A technical guide to connecting Torii to AI agents using Truto's /tools endpoint. Learn how to automate IT lifecycles, shadow IT discovery, and contract management.

Uday Gajavalli Uday Gajavalli · · 10 min read
Connect Torii to AI Agents: Automate SaaS Lifecycles and App Data

You want to connect Torii to an AI agent so your system can automatically read user directories, execute SaaS offboarding, audit shadow IT, and reconcile contract data. Here is exactly how to do it using Truto's /tools endpoint and SDK, bypassing the need to build and maintain a custom connector from scratch.

IT teams are drowning in SaaS sprawl. A March 2026 industry benchmark report indicates that the average mid-market enterprise manages over 300 unique SaaS applications, with identities constantly shifting due to onboarding, role changes, and departures. The industry is rapidly shifting toward agentic IT automation - autonomous systems that work alongside sysadmins to execute multi-step governance workflows. If your team uses ChatGPT, check out our guide to connecting Torii to ChatGPT, or if you are building exclusively on Anthropic's models, read our guide to connecting Torii to Claude. For developers building custom multi-agent workflows, you need a programmatic way to fetch these tools and bind them to your agent framework.

Giving a Large Language Model (LLM) read and write access to your Torii instance is an engineering challenge. You either spend months building, hosting, and maintaining a custom connector that handles complex pagination and rate limits, or you use a managed infrastructure layer to overcome the SaaS integration bottleneck.

This guide breaks down exactly how to fetch AI-ready tools for Torii, bind them natively to an LLM using frameworks like LangChain or Vercel AI SDK, and execute complex IT governance workflows autonomously.

The Engineering Reality of Torii's API

Building AI agents is trivial. Connecting them to external SaaS APIs safely is a completely different domain. Giving an LLM access to external data sounds simple in a notebook prototype: you write a Node.js function that makes a fetch request and wrap it in an @tool decorator. In production, this approach collapses entirely under the weight of real-world API constraints.

Torii's API introduces several specific integration challenges that break standard CRUD assumptions and trip up naive LLM implementations.

The Junction Object Complexity

Torii maps an extremely complex graph of entities: Users, Applications, and Contracts. Unlike a flat CRM structure, Torii relies heavily on junction objects. If an agent wants to know what apps a user has, it cannot just inspect the user record. It must query user-applications, a distinct junction object connecting id_user and id_app.

When you ask an LLM to "remove a user from Slack", it often hallucinates an update to the user record itself. To execute this correctly, the agent must first list the user's applications, locate the specific id for the Slack association, and then update the isUserRemovedFromApp flag on that specific junction object. Providing poorly defined tools will result in the LLM mutating the wrong data.

Variable Rate Limits and 429 Handling

Torii enforces strict, endpoint-specific rate limits. For example, creating or updating contracts is capped at 800 requests per minute, while updating apps drops to 200 requests per minute.

If your AI agent goes into a loop attempting to reconcile a massive list of discovered apps against active contracts, it will quickly hit a rate limit and Torii will return an HTTP 429 Too Many Requests error.

Critical Architectural Note: Truto does not retry, throttle, or apply backoff on rate limit errors automatically. Doing so inside an integration proxy leads to opaque timeouts and broken agent execution traces. For more on this pattern, refer to our guide on how to handle third-party API rate limits. When Torii returns a 429, Truto passes that error directly back to the caller. Truto normalizes the upstream rate limit information into standardized HTTP headers per the IETF specification:

  • ratelimit-limit: The maximum allowed requests.
  • ratelimit-remaining: The number of requests left in the current window.
  • ratelimit-reset: The time at which the rate limit window resets.

The caller (your agent's execution loop) is strictly responsible for inspecting these headers, invoking a sleep or exponential backoff, and retrying the tool call.

The Catalog vs. Tenant Dichotomy

Torii separates the concept of its global software catalog from the apps actually installed in a tenant's environment. The list_all_torii_apps endpoint returns only the apps currently active or discovered within the specific organization. However, the torii_apps_search endpoint searches Torii's global database of known software. If an agent is tasked with adding a new contract for a tool the company hasn't historically used, it must first search the catalog to find the correct idApp before it can attach a contract to it.

Auto-Generating Torii Tools via Truto

Every integration on Truto is represented as a comprehensive JSON object mapping the underlying API's behavior. We define Resources (like Users, Apps, Contracts) and Methods on those resources (List, Get, Create, Update).

Truto handles the authentication handshake, API schema normalization, and pagination cursors. To expose these to an LLM, Truto provides a /tools endpoint that translates these Methods into fully described, JSON-schema-compliant function definitions, making it one of the best unified APIs for LLM tool calling.

To fetch your Torii tools, you simply make a GET request to your integrated account:

GET https://api.truto.one/integrated-account/<torii_account_id>/tools
Authorization: Bearer <truto_api_key>

This returns an array of proxy APIs with their descriptions and query schemas perfectly formatted for LLM ingestion.

Hero Tools for Torii AI Agents

To orchestrate SaaS lifecycles effectively, you do not need to expose all 50+ Torii endpoints to the LLM. Doing so often pollutes the context window. Instead, you should provide a highly curated set of high-leverage tools.

Here are the critical "hero tools" to inject into your Torii agent.

get_single_torii_user_by_id

Retrieves the core identity record for an employee. The agent relies on this to inspect the lifecycleStatus (e.g., Active, Suspended, Deleted) and verify external identity source state (isDeletedInIdentitySources).

Usage Note: Agents should always use this to confirm a user's current state before attempting any write operations or offboarding workflows.

"Retrieve the Torii profile for the user ID 8f72a1b9 to check if their lifecycle status is already marked as offboarded."

update_a_torii_user_by_id

Modifies a specific user in Torii. This is the primary execution tool for HR-driven IT lifecycle changes. It requires the lifecycleStatus in the request body.

Usage Note: When offboarding, the agent must change the status here. The response returns the updated state confirming the execution.

"Update the Torii user ID 8f72a1b9 and set their lifecycleStatus to Suspended."

list_all_torii_user_applications

Lists the application junction objects for a specific user. Requires id_user. This returns the critical isUserRemovedFromApp and state fields for every app tied to that identity.

Usage Note: Because Torii tracks app assignments separately from the user record, the agent must pull this list to generate a comprehensive offboarding checklist or security audit.

"List all user applications associated with Torii user ID 8f72a1b9 so I can verify which SaaS tools they still have active access to."

Searches Torii's global catalog of software using a query string. Returns the global id, category, and url.

Usage Note: If an agent is processing an inbound software request from Jira, it must use this tool to map the plain-text software name (e.g., "Figma") to the canonical Torii app ID before attempting to log a contract.

"Search the Torii app catalog for 'Figma' and return the official application ID and category."

list_all_torii_contracts

Returns an array of all financial contracts in the organization, including id, name, status, and idApp.

Usage Note: Used heavily by financial and procurement agents to reconcile upcoming renewals against actual utilization data.

"List all Torii contracts so I can identify which ones are currently marked as Active and tied to the Figma application ID."

update_a_torii_contract_by_id

Modifies contract details. Supports updating the status and numeric/object-based currency fields.

Usage Note: An agent processing a renewal via email or ticketing system can invoke this to update the contract value or push the expiration date forward.

"Update the Torii contract ID c991a2 to reflect a renewed status and adjust the currency amount to 15000."

For the complete inventory of available methods, supported properties, and granular schema details, refer to the Torii integration page.

Workflows in Action

Exposing these tools to an LLM unlocks multi-step, autonomous operations that previously required hours of manual point-and-click work from sysadmins. Here is how these tools sequence together in production.

1. Autonomous Employee Offboarding

When a termination ticket hits the IT queue, an agent can autonomously handle the SaaS teardown checklist.

"A departure ticket was created for jsmith@company.com. Find their Torii user ID, audit all the applications they currently have access to, and update their Torii lifecycle status to suspended."

Execution Steps:

  1. list_all_torii_users: The agent searches for the email jsmith@company.com to extract the Torii id.
  2. list_all_torii_user_applications: The agent passes the extracted id_user to pull an array of all SaaS apps currently assigned to the user.
  3. (Internal Logic): The agent formats this list of active apps into an audit log to paste back into the IT ticket.
  4. update_a_torii_user_by_id: The agent executes the state change, mutating the lifecycleStatus to Suspended.

Result: The user is suspended globally in Torii, and the IT admin receives an automated comment on their ticket detailing exactly which applications the user possessed at the time of suspension.

2. Shadow IT Discovery & Contract Mapping

Financial controllers often struggle to map rogue software expenses to official catalog items. An agent can read expense data and map it directly into Torii.

"We found an unmanaged expense for 'Lucidchart'. Search the Torii catalog to find the official ID for this app, then check if we already have an active contract for it in our system."

Execution Steps:

  1. torii_apps_search: The agent queries q=Lucidchart and retrieves the canonical id from Torii's global database.
  2. list_all_torii_contracts: The agent retrieves the organization's contracts.
  3. (Internal Logic): The agent filters the contract array looking for the idApp that matches the Lucidchart ID.

Result: The agent replies informing the controller whether this software is genuinely rogue (no contract exists) or if it simply represents off-contract spend on an existing approved vendor.

Building Multi-Step Workflows

To build a resilient AI agent, you must construct an execution loop capable of passing tool schemas to the model, catching the model's tool calls, invoking the Truto API, and feeding the result back into the prompt context.

Crucially, this loop must intercept 429 rate limit errors and utilize the ratelimit-reset header, as Truto will not absorb these errors for you.

Below is an architectural example using TypeScript and the official @langchain/core primitives. We assume the use of a utility manager (like the TrutoToolManager from our truto-langchainjs-toolset) to handle the raw /tools fetching.

import { ChatOpenAI } from "@langchain/openai";
import { TrutoToolManager } from "truto-langchainjs-toolset";
import { HumanMessage, AIMessage, ToolMessage } from "@langchain/core/messages";
 
async function runToriiAgent(prompt: string, toriiAccountId: string) {
  // 1. Initialize the LLM
  const llm = new ChatOpenAI({
    modelName: "gpt-4o",
    temperature: 0,
  });
 
  // 2. Fetch Torii tools from Truto
  const toolManager = new TrutoToolManager(process.env.TRUTO_API_KEY);
  const tools = await toolManager.getTools(toriiAccountId);
  
  // Bind the tools to the LLM
  const llmWithTools = llm.bindTools(tools);
 
  // 3. Setup conversation state
  const messages = [new HumanMessage(prompt)];
 
  console.log("Starting autonomous Torii agent execution...");
 
  // 4. The Agent Execution Loop
  while (true) {
    const response = await llmWithTools.invoke(messages);
    messages.push(response);
 
    // If the model decides no more tools are needed, break the loop
    if (!response.tool_calls || response.tool_calls.length === 0) {
      console.log("\nFinal Output:", response.content);
      break;
    }
 
    // 5. Execute requested tool calls
    for (const toolCall of response.tool_calls) {
      console.log(`Executing Torii Tool: ${toolCall.name}`);
      
      const toolToRun = tools.find((t) => t.name === toolCall.name);
      if (!toolToRun) continue;
 
      let toolResultStr = "";
 
      try {
        // Execute the proxy API call via the Truto tool definition
        const toolResult = await toolToRun.invoke(toolCall.args);
        toolResultStr = JSON.stringify(toolResult);
        
      } catch (error: any) {
        // 6. Explicitly handle HTTP 429 Rate Limits from Torii via Truto
        if (error.response && error.response.status === 429) {
          console.warn(`[Rate Limit Hit] Torii returned 429 for ${toolCall.name}.`);
          
          const headers = error.response.headers;
          const resetTimeMs = parseInt(headers['ratelimit-reset'], 10) * 1000;
          const now = Date.now();
          const sleepDuration = Math.max(0, resetTimeMs - now) + 1000; // buffer
 
          console.log(`Sleeping for ${sleepDuration}ms based on ratelimit-reset header...`);
          await new Promise(resolve => setTimeout(resolve, sleepDuration));
 
          // Instruct the LLM that the tool failed due to limits and it should try again
          toolResultStr = JSON.stringify({ 
            error: "Rate limit exceeded. The system paused. Please retry your tool call immediately." 
          });
        } else {
          // Handle 400s, 401s, etc.
          toolResultStr = JSON.stringify({ error: error.message });
        }
      }
 
      // Feed the result (or the rate limit instruction) back to the LLM
      messages.push(new ToolMessage({
        tool_call_id: toolCall.id,
        content: toolResultStr,
      }));
    }
  }
}
 
// Execute an audit workflow
runToriiAgent(
  "List all Torii users, find the user named 'Alex Chen', and then list all their applications.", 
  "torii_acct_8891abc"
);

Understanding the Architecture

sequenceDiagram
    participant User
    participant Agent as LLM Framework
    participant Truto as Truto /tools API
    participant Torii as Torii Upstream API

    User->>Agent: "Offboard user Alex Chen"
    Agent->>Truto: GET /integrated-account/{id}/tools
    Truto-->>Agent: Returns JSON Schema (list_all_torii_users, etc.)
    Agent->>Agent: LLM analyzes schemas and generates tool_call
    Agent->>Truto: POST proxy execute (list_all_torii_users)
    Truto->>Torii: Normalized API Request
    Torii-->>Truto: 200 OK (User Data)
    Truto-->>Agent: JSON Tool Response
    Agent->>Agent: LLM decides to execute update_a_torii_user_by_id
    Agent->>Truto: POST proxy execute (update_a_torii_user)
    Truto->>Torii: Normalized Write Request
    alt Rate Limit Reached
        Torii-->>Truto: 429 Too Many Requests
        Truto-->>Agent: 429 + ratelimit-reset headers
        Agent->>Agent: Parses headers, triggers sleep()
        Agent->>Truto: Retries POST proxy execute
    end
    Torii-->>Truto: 200 OK (User Updated)
    Truto-->>Agent: JSON Tool Response
    Agent-->>User: "Alex Chen has been suspended."

By separating the tool definition from the LLM execution logic, you ensure that your agent stays agnostic to Torii's specific authentication mechanisms and pagination formats, interacting solely with clean, standardized JSON objects.

Moving from Script to Production

Attempting to build and maintain custom wrappers for the Torii API will trap your engineering team in a perpetual cycle of reading vendor docs, updating JSON schemas, and fighting edge cases around pagination and endpoint routing.

By using Truto's Unified API and Proxy Architecture, you instantly generate AI-ready tools that map perfectly to the underlying SaaS data models. You retain full control over your agent's execution loop and rate limit handling, without maintaining the integration boilerplate.

FAQ

Does Truto automatically handle Torii API rate limits for my AI agent?
No. Truto passes HTTP 429 errors directly to the caller and normalizes the upstream rate limit information into standard IETF headers (ratelimit-limit, ratelimit-remaining, ratelimit-reset). Your agent execution loop must implement its own retry and backoff logic.
Can I connect Torii to custom LLM frameworks other than LangChain?
Yes. Truto's /tools endpoint returns standard JSON schemas for every proxy API. You can consume these schemas and bind them to any framework, including Vercel AI SDK, CrewAI, or raw OpenAI function calls.
How do AI agents handle the difference between users and application users in Torii?
Torii separates global organization users from app-specific users. Agents must use the specific list_all_torii_application_users tool when auditing who has access to a specific application, rather than relying on the global user directory.

More from our Blog