Skip to content

Connect Indeed SCIM to AI Agents: Orchestrate Full User Provisioning

Learn how to connect Indeed SCIM to AI Agents using Truto's /tools endpoint. Bypass complex SCIM URN schemas and safely orchestrate identity and user provisioning workflows.

Sidharth Verma Sidharth Verma · · 9 min read
Connect Indeed SCIM to AI Agents: Orchestrate Full User Provisioning

You want to connect Indeed SCIM to an AI agent so your internal systems can independently manage user identities, audit employer organization assignments, and execute full lifecycle provisioning based on chat input or event triggers. Here is exactly how to do it using Truto's /tools endpoint and SDK, bypassing the need to maintain fragile, custom-built identity wrappers.

Giving a Large Language Model (LLM) read and write access to an enterprise SCIM provider like Indeed is an engineering minefield. You either spend weeks reading RFCs, mapping complex URN schemas, and handling edge cases, or you use a managed infrastructure layer that standardizes the integration surface. If your team uses ChatGPT, check out our guide on connecting Indeed SCIM to ChatGPT, or if you are building on Anthropic's models, read our guide on connecting Indeed SCIM to Claude. For developers building custom autonomous workflows, you need a programmatic way to fetch these tools and bind them to your agent framework.

This guide breaks down exactly how to fetch AI-ready tools for Indeed SCIM, bind them natively to an LLM using your preferred framework (LangChain, LangGraph, CrewAI, Vercel AI SDK), and safely execute identity operations. For a deeper look at the architecture behind this tool-layer approach, refer to our research on architecting AI agents and the SaaS integration bottleneck.

The Engineering Reality of Indeed SCIM Integrations

Building AI agents is trivial; giving them safe, deterministic access to external SaaS APIs is not. SCIM (System for Cross-domain Identity Management) was designed to standardize identity provisioning across the web. In practice, every vendor implements it with unique constraints. If you hand-code an Indeed SCIM connector for your agent, you are responsible for mitigating several API-specific traps that will reliably cause an LLM to hallucinate or corrupt user state.

The URN Schema Trap

Indeed SCIM heavily relies on standard and extended SCIM URN namespaces. For example, assigning a user to an employer organization requires injecting data into the urn:ietf:params:scim:schemas:extension:indeed:2.0:EmployerOrg namespace. If you expose raw API docs to an LLM, it will invariably hallucinate these strings, misplace nested arrays, or invent namespaces like urn:indeed:org. A unified tool layer enforces these schemas via strict JSON validation before the request ever hits the network.

The "List" Endpoint is Not a List

The Indeed SCIM List Users endpoint is deceiving. Unlike standard REST endpoints that return paginated arrays of all resources, this specific endpoint functions strictly as an exact-match query. It requires a filter parameter (by externalId or email) and is designed to return a maximum of one user. If the filter matches multiple users, the API throws an error rather than paginating the results. If your agent attempts a generic GET /Users assuming it will receive a directory dump, it will crash.

The Full-Overwrite Update Danger

In many REST APIs, a PATCH request allows you to update a single field, like a user's title. Indeed SCIM relies on PUT requests for user updates (update_a_indeed_scim_user_by_id), which execute a complete overwrite. Every value on the account is replaced. If an attribute is omitted from the payload, it is wiped on the server. If your agent decides to update a user's email and sends a JSON object containing only the email, it will successfully wipe the user's name, external ID, and employer organization memberships.

Asynchronous Deletion Latency

Identity deletion in distributed systems is rarely instant. When deleting an Indeed SCIM user via delete_a_indeed_scim_user_by_id, the user is immediately removed from any employer organizations, but the actual deletion process can take up to 30 minutes to propagate. If you program an agent to execute a delete and immediately run a read check to assert the user is gone, it will encounter stale data, assume the deletion failed, and retry - potentially creating a loop.

Exposing Indeed SCIM to Agents: The Unified Tool Layer

To prevent the LLM from triggering these failure modes, you must decouple the model from the raw HTTP layer. Truto achieves this by mapping the underlying API into Resources (endpoints) and Methods (operations on those endpoints).

When building agentic workflows, you do not want to force your LLM to act as a data integration engineer. By calling Truto's /integrated-account/:id/tools endpoint, you receive a dynamic catalog of Proxy APIs representing the specific Methods configured for Indeed SCIM. These tools encapsulate authentication, schema validation, and URL construction, presenting the agent with simple, deterministic function signatures.

sequenceDiagram
    participant Agent as Agent Framework
    participant Truto as Truto /tools API
    participant Indeed as Indeed SCIM API

    Agent->>Truto: GET /integrated-account/<id>/tools
    Truto-->>Agent: Returns normalized JSON schemas for Indeed methods
    Agent->>Agent: .bindTools(schemas) to LLM
    Note over Agent: LLM decides to call<br>update_a_indeed_scim_user_by_id
    Agent->>Truto: POST tool execution (JSON payload)
    Truto->>Truto: Validates against strict JSON schema
    Truto->>Indeed: Translates to standard SCIM URN payload
    Indeed-->>Truto: 200 OK (Full Overwrite Success)
    Truto-->>Agent: Formatted JSON response

A Crucial Note on Rate Limits

When an AI agent operates autonomously, it can generate API traffic at a massive scale. It is critical to understand that Truto does not automatically retry, throttle, or apply backoff on rate limit errors.

When the Indeed SCIM API returns an HTTP 429 Too Many Requests error, Truto passes that error directly to the caller. However, Truto standardizes the upstream rate limit information into IETF-compliant headers: ratelimit-limit, ratelimit-remaining, and ratelimit-reset. Your agent execution loop is fully responsible for intercepting the 429, reading the ratelimit-reset header, pausing execution, and retrying. We will cover this specific backoff implementation in the workflow section below.

Hero Tools for Indeed SCIM

Truto exposes highly specific tools tailored to the exact requirements of the Indeed SCIM API. By narrowing the LLM's operational scope to these distinct functions, you drastically reduce the surface area for hallucinations.

list_all_indeed_scim_users

This tool retrieves a specific Indeed SCIM user by filtering on externalId or email. Because of the API's strict behavior, this tool is guaranteed to return at most one user record, making it an excellent deterministic lookup tool before executing updates.

Contextual usage notes: The agent must provide a valid filter parameter. If the agent attempts a wildcard search that yields multiple results, the tool will intentionally return an error to prevent ambiguous data mapping.

"Look up the user with the email address j.doe@company.com in Indeed SCIM to retrieve their encrypted account ID for further processing."

get_single_indeed_scim_user_by_id

Retrieves a single Indeed SCIM user using their encrypted account ID, specifically including all associated employer organization URN data.

Contextual usage notes: This tool is the mandatory precursor to any update operation. The agent must use this to fetch the complete current state of the user so it can modify specific fields locally before pushing a full overwrite.

"Fetch the complete Indeed SCIM profile for user ID a1b2c3d4, including their current EmployerOrg extensions, so we can prepare an update payload."

create_a_indeed_scim_user

Creates a new Indeed SCIM user, handling the complex nested payload requirements such as primary emails, naming conventions, and optional employer organization mapping.

Contextual usage notes: The LLM does not need to understand SCIM URNs. Truto's JSON schema forces the model to supply plain fields (e.g., givenName, familyName, primaryEmail), and the proxy layer handles the structural translation.

"Provision a new Indeed SCIM user for Jane Smith with external ID EMP-90210 and assign them a standard userType. Ensure their primary email is set to jane.smith@company.com."

update_a_indeed_scim_user_by_id

Overwrites an existing Indeed SCIM user record.

Contextual usage notes: This is a full overwrite tool. Agents must be strictly prompted to include the entire user state in the request body. If the agent omits the urn:ietf:params:scim:schemas:extension:indeed:2.0:EmployerOrg object during the update, the user will be stripped of their organizational ties.

"Take the user data we just retrieved, change their familyName to Johnson, and submit the complete updated profile to update_a_indeed_scim_user_by_id."

delete_a_indeed_scim_user_by_id

Initiates the asynchronous deletion of a user and severs their organizational memberships.

Contextual usage notes: The tool returns a 204 No Content on success. Agents must be instructed that this process takes up to 30 minutes, so they should not immediately loop back and verify the deletion via a lookup tool.

"Deprovision the user ID a1b2c3d4 from Indeed SCIM. Acknowledge that the deletion will process asynchronously over the next 30 minutes."

For the complete tool inventory and granular OpenAPI schema details, refer to the Indeed SCIM integration page.

Building Multi-Step Workflows

To build autonomous identity workflows, you must write an execution loop that binds Truto tools to your model, manages context, and handles network realities like rate limiting. The following TypeScript example demonstrates how to integrate Truto tools with a framework-agnostic executor loop, implementing the mandatory backoff logic for HTTP 429s.

First, we build a utility to fetch the tools from Truto and map them to our agent framework (using LangChain's structure as an example).

import { DynamicStructuredTool } from "@langchain/core/tools";
import { z } from "zod";
 
// Utility to fetch tools from Truto
export async function getIndeedScimTools(integratedAccountId: string, trutoApiKey: string) {
  const response = await fetch(
    `https://api.truto.one/integrated-account/${integratedAccountId}/tools?methods[0]=custom&methods[1]=read&methods[2]=write`,
    {
      headers: { Authorization: `Bearer ${trutoApiKey}` }
    }
  );
 
  if (!response.ok) throw new Error("Failed to fetch Truto tools");
  const toolsData = await response.json();
 
  return toolsData.map((tool: any) => {
    return new DynamicStructuredTool({
      name: tool.name,
      description: tool.description,
      // Truto returns a JSON schema, which you can map to Zod dynamically
      // (Implementation of jsonSchemaToZod omitted for brevity)
      schema: jsonSchemaToZod(tool.query_schema),
      func: async (args) => executeTrutoTool(tool.url, args, trutoApiKey)
    });
  });
}

Next, we implement the execution layer that actually makes the network request to Truto. Because Truto explicitly delegates rate limit handling to the caller, we must inspect the ratelimit-reset header when encountering a 429 and implement a delay.

async function executeTrutoTool(url: string, args: any, apiKey: string, retryCount = 0): Promise<any> {
  const response = await fetch(url, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${apiKey}`
    },
    body: JSON.stringify(args)
  });
 
  // Handle HTTP 429 Too Many Requests based on IETF headers
  if (response.status === 429) {
    if (retryCount >= 3) throw new Error("Rate limit backoff exceeded");
    
    // Truto returns UNIX epoch seconds in ratelimit-reset
    const resetHeader = response.headers.get("ratelimit-reset");
    const resetTime = resetHeader ? parseInt(resetHeader, 10) * 1000 : Date.now() + 5000;
    const waitTime = Math.max(0, resetTime - Date.now());
 
    console.warn(`Rate limited. Backing off for ${waitTime}ms...`);
    await new Promise((resolve) => setTimeout(resolve, waitTime));
    
    return executeTrutoTool(url, args, apiKey, retryCount + 1);
  }
 
  if (!response.ok) {
    throw new Error(`Tool execution failed: ${response.statusText}`);
  }
 
  return response.json();
}

By handling the 429 gracefully at the function execution level, your LangGraph or Vercel AI SDK loops will simply experience a slight delay rather than crashing out and losing their conversation state.

Workflows in Action

When the LLM has access to these specific tools, it can orchestrate complex, multi-step identity workflows that historically required brittle custom scripts.

Use Case 1: Automated Employee Offboarding

When an employee leaves, IT needs to ensure their access is revoked cleanly without leaving orphaned records.

"Offboard John Doe. His email is j.doe@company.com. Find his record in Indeed SCIM and delete it. Remember that deletion takes 30 minutes, so do not try to verify it immediately."

Execution Steps:

  1. list_all_indeed_scim_users: The agent constructs a filter email eq "j.doe@company.com". It receives the encrypted account ID in the response.
  2. delete_a_indeed_scim_user_by_id: The agent passes the ID to the delete tool. It receives a 204 No Content.
  3. Formatting Response: The agent concludes the workflow, informing the user that the deletion has been triggered and will finish propagating within 30 minutes.

Use Case 2: Just-In-Time Org Assignment and Safe Updates

When an employee shifts departments, their external metadata and employer organization must be updated without destroying the rest of their profile.

"Update Sarah Smith's account (s.smith@company.com). Change her externalId to DEPT-ENG-404. Ensure you preserve her current EmployerOrg assignments and all other profile data during the update."

Execution Steps:

  1. list_all_indeed_scim_users: The agent looks up the user by email to acquire the ID.
  2. get_single_indeed_scim_user_by_id: The agent fetches the complete user profile using the ID. The response includes the complex urn:ietf:params:scim:schemas:extension:indeed:2.0:EmployerOrg data.
  3. Memory Modification: The LLM parses the JSON context, replaces the externalId value, and retains the exact structure of the rest of the payload.
  4. update_a_indeed_scim_user_by_id: The agent executes the full-overwrite PUT request using the modified complete profile.
  5. Formatting Response: The agent returns confirmation that the ID was updated without state destruction.
flowchart TD
    A["User Prompt:<br>'Update Sarah's ID'"] --> B["Agent Tool:<br>list_all_indeed_scim_users"]
    B --> C["Agent Tool:<br>get_single_indeed_scim_user_by_id"]
    C --> D["Agent modifies JSON state in memory"]
    D --> E["Agent Tool:<br>update_a_indeed_scim_user_by_id"]
    E --> F["Return success to user"]

The Path to Deterministic AI Operations

Connecting LLMs to identity providers like Indeed SCIM is not a prompt engineering challenge; it is an integration architecture challenge. Direct API access forces the LLM to navigate unpaginated list filters, URN schema variations, and the destructive nature of full-overwrite endpoints.

By leveraging Truto's /tools endpoint, you isolate the LLM from these network realities. The agent operates strictly on standardized JSON schemas, while your execution environment handles IETF-compliant rate limit backoffs and schema translations. This guarantees that when your agent decides to manage a user's lifecycle, it does so deterministically, securely, and exactly as designed.

FAQ

How do AI agents handle Indeed SCIM's full-overwrite update requirements?
Indeed SCIM requires full payload overwrites for user updates. Truto's AI tools provide strict JSON schemas that force the agent to retrieve the full user record first, modify the target fields, and pass the complete state back to the update tool, preventing accidental data loss.
Does Truto automatically handle Indeed SCIM rate limits for AI agents?
No. Truto passes upstream HTTP 429 errors directly to the caller. However, Truto normalizes the rate limit information into standard IETF headers (ratelimit-limit, ratelimit-remaining, ratelimit-reset), allowing your agent's executor loop to implement precise retry and backoff logic.
Can I use these Indeed SCIM tools with frameworks other than LangChain?
Yes. Truto's /tools endpoint returns OpenAPI-compliant JSON schemas for every method. This makes the tools framework-agnostic, meaning you can bind them to LangGraph, CrewAI, Vercel AI SDK, or custom executor loops.

More from our Blog