Skip to content

Connect Justworks to AI Agents: Sync Paystubs & Time Off Reporting

Nachi Raman Nachi Raman 11 min read AI & Agents
TrutoFor teams building AI agents

Give your AI agent Justworks tools.

Connect Justworks to AI agents to automate payroll, deductions, and time-off reporting. This guide covers API quirks, rate limits, tool calling, and multi-step workflows using Truto.

In this guide

  1. 01Determine Tool Layer Architecture
  2. 02Handle Justworks API Formatting Requirements
  3. 03Fetch AI-Ready Tools from Truto
  4. 04Bind Tools to the LLM
  5. 05Implement Rate Limit and Error Handling
Use Justworks in your own ChatGPT or Claude. Elaichi, from the team behind Truto, free for 14 days. Try Elaichi

The guide

Learn how to connect Justworks to AI agents using Truto. Discover how to safely handle payroll tools, time-off reports, and HR data with LLMs.

You want to connect Justworks to an AI agent so your system can independently fetch member data, sync paystubs, audit time-off policies, and trigger custom payroll deductions based on external business logic. Here is exactly how to do it using Truto's /tools endpoint and SDK, bypassing the need to write and maintain complex HRIS integration code from scratch.

Giving a Large Language Model (LLM) read and write access to a payroll and human resources platform is a high-stakes engineering challenge. You cannot afford hallucinations when dealing with compensation data, tax IDs, or employee status. If your team uses ChatGPT, check out our guide on connecting Justworks to ChatGPT, or if you are building on Anthropic's models, read our guide on connecting Justworks to Claude. For developers building custom autonomous workflows, you need a programmatic way to fetch these tools, constrain their inputs, and bind them to your agent framework.

This guide breaks down exactly how to fetch AI-ready tools for Justworks, bind them natively to an LLM using frameworks like LangChain, LangGraph, CrewAI, or the Vercel AI SDK, and execute complex HR workflows safely. For a deeper look at the architecture behind this approach, refer to our research on architecting AI agents and the SaaS integration bottleneck.

Why a Unified Tool Layer Matters for Agent Safety

Before writing a line of integration code, you must decide what layer your agent talks to. When dealing with an HRIS like Justworks, this choice determines the safety and reliability of your production system.

Direct API tools - mapping one tool per raw Justworks endpoint - seem fast to prototype but push severe vendor quirks directly into the LLM's context window. The model has to remember that Justworks formats currency differently than percentages, that member objects change shape depending on token scopes, and that certain endpoints require specific query filters just to operate. Every one of those quirks is a hallucination waiting to happen.

A unified proxy layer collapses these endpoints behind a strict, predictable schema. Your agent sees cleanly defined functions like list_all_justworks_members, create_a_justworks_deduction, and get_single_justworks_paystub_by_id. That gives you concrete safety wins:

  1. Smaller attack surface for hallucination. The LLM only chooses from defined, stable function names. It never invents endpoint paths or malformed payload structures.
  2. Deterministic input validation. Every tool has a strict JSON schema. Invalid arguments (like sending a string for a gross pay value instead of an integer) are rejected before they ever touch the Justworks API, allowing the agent to self-correct.
  3. Isolated authentication. The agent never sees bearer tokens, client secrets, or OAuth refresh tokens. The infrastructure handles the token lifecycle, completely removing credential leakage from the LLM threat model.

The Engineering Reality of the Justworks API

Giving an LLM access to external HR data sounds simple in a notebook environment. You write a Node.js function that makes a fetch request and wrap it in a tool decorator. Against a production system like Justworks, this approach collapses under the weight of vendor-specific API design.

If you hardcode these interactions into your agent, you will spend your engineering cycles writing defensive integration code instead of improving your model's reasoning capabilities. Here is what makes the Justworks API specifically challenging for AI agents.

The Silent Omission of Unscoped Fields

Justworks relies on highly granular OAuth scopes (member.pay:read, member.dob:read, member.employment:read). When you query a member object, the API does not throw an error if you request a field you lack scope for - it simply omits the field from the JSON response entirely. It also silently omits any fields that have empty values.

LLMs are terrible at handling silently missing keys. If an agent is tasked with calculating average compensation and the current_pay field is missing due to a scope issue, standard agents will often hallucinate a plausible salary based on the job title rather than recognizing the permission failure. Your tool descriptions must explicitly instruct the LLM on how to handle missing data points, and your proxy layer must enforce schema boundaries.

Strict Amount Formatting and Floating-Point Math

Payroll APIs are unforgiving regarding currency formatting. Justworks uses a strict integer-based system for amounts to avoid floating-point math errors. All fixed monetary amounts are expected in cents (e.g., sending 4500 for $45.00).

However, the API breaks this pattern for percentage deductions. If an amount type is percent, the value must carry exactly 4 decimal places (e.g., sending 37000 for 3.7%). Standard LLMs naturally default to decimals for currency and standard floats for percentages. If an agent sends 45 to a fixed deduction, it just deducted 45 cents instead of 45 dollars. The tool schema must strongly type and rigorously validate these formatting rules before the payload hits the network.

The Partial Success Bulk Update Trap

When creating or updating deductions via the /deductions or /deductions/bulk_update endpoints, Justworks accepts an array of items. When the request processes, Justworks returns a 201 Created or 200 OK status code - even if some or all of the individual entries in the array failed.

AI agents generally rely on HTTP status codes to determine tool success. If an agent sees a 201, it assumes the job is done and reports success to the user. To prevent this, your integration layer must intercept the 201 response, parse the returned items array, and explicitly check the success boolean on every single entry. If any entry shows success: false with an error_message, the tool must return a formatted error string back to the LLM so it can correct the payload and retry.

Handling Rate Limits in Production

When automating workflows across hundreds of employees, your agent will eventually hit Justworks API rate limits.

It is critical to understand that Truto does not retry, throttle, or apply backoff on rate limit errors. Truto's architecture is designed for transparency. When the upstream Justworks API returns an HTTP 429 Too Many Requests, Truto passes that error directly to the caller.

To make handling these limits standardized, Truto normalizes the upstream rate limit information into standard IETF HTTP headers regardless of how the vendor originally formatted them. You will receive:

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

Your AI agent framework or calling code is entirely responsible for reading these headers, pausing execution, and retrying the tool call. Do not assume the infrastructure will magically absorb rate limits for you.

High-Leverage AI Agent Tools for Justworks

Instead of building individual endpoints, Truto maps the Justworks API into Proxy APIs and exposes them dynamically via the /tools endpoint. Below are the most effective hero tools for HR and payroll automation.

list_all_justworks_members

This tool retrieves the members (employees, contractors, owners) of the connected Justworks company. It supports cursor pagination and filtering by status or last updated date. Because fields are scope-gated, this is the foundational tool for auditing your workforce.

Contextual Usage Notes: Remind the agent that missing fields (like current_pay or emails) mean the value is either empty or the connection lacks the required read scope. Always filter by status=active when calculating current payroll metrics.

"Fetch all active employees updated in the last 30 days and extract their current department and manager IDs. If the manager ID is missing, flag the record for review."

get_single_justworks_paystub_by_id

Retrieves the detailed line items of a specific paystub, including earnings, employee deductions, and employer contributions.

Contextual Usage Notes: This tool requires a paystub_id, which must first be obtained by listing payrolls and then listing paystubs for that payroll. Ensure the agent knows all monetary amounts returned are integers representing cents.

"Retrieve the detailed paystub for ID paystub_12345. Calculate the total employer contributions in dollars and compare it against the gross pay."

create_a_justworks_deduction

Creates one or more payroll deductions in a single call. This is vital for syncing external benefit programs, equipment charges, or custom garnishments.

Contextual Usage Notes: The agent must format fixed amounts in cents and percent amounts with 4 decimal places. The tool response will contain an array of items; the agent must verify the success flag for every item submitted.

"Create a new fixed deduction of $50.00 for gym membership for member_67890 starting on 2024-05-01. Ensure the amount is formatted as cents before submitting."

justworks_deductions_bulk_update

Updates existing deductions. Useful for annual benefit enrollment changes or correcting errors across multiple employees at once.

Contextual Usage Notes: Requires the specific deduction_id. The agent should only send the fields that need changing. Like creation, it must parse the response array to confirm successful updates.

"Update the existing 401k deduction for deduction_abc123 to a new percent amount of 4.5%. Verify the update succeeded by checking the response payload."

list_all_justworks_time_off_requests

Lists time-off requests filtered by date range, status, or specific member. This tool powers capacity planning and PTO liability reporting.

Contextual Usage Notes: The start_date and end_date parameters are strictly required and must be formatted as YYYY-MM-DD. Pay close attention to the unit_type field (minutes, hours, or days) when aggregating total time off.

"List all approved time-off requests between 2024-06-01 and 2024-06-30. Sum the total amount of time off taken, converting all values into standard 8-hour days."

To view the complete inventory of Justworks tools, including company jurisdictions, custom fields, and payroll fees, visit the Justworks integration page.

Workflows in Action

Building single-tool prompts is easy. Orchestrating multi-step workflows that execute actual HR operations requires chaining tool outputs to subsequent tool inputs. Here are two concrete examples of how an agent uses these tools in the real world.

Scenario 1: End-of-Month Deduction Reconciliation

The Prompt:

"Find the active employee named Jane Doe. Once you have her member ID, create a new one-time fixed deduction of $125.00 for 'Equipment Fee'. Confirm if the deduction was successfully applied."

Agent Execution Sequence:

  1. list_all_justworks_members: The agent calls this tool, filtering (or paging through) to locate the record where name matches "Jane Doe" and active is true. It extracts id (e.g., member_98765).
  2. list_all_justworks_deduction_types: The agent queries available deduction codes to find the exact string required by Justworks for equipment fees (e.g., equip_fee_01).
  3. create_a_justworks_deduction: The agent constructs the payload. It formats $125.00 as 12500 (cents), sets amount_type to fixed, and sets the member_id.
  4. Verification: The tool returns a 201 response with an array. The agent reads the inner object, confirms success: true, and replies to the user.

Output: The HR admin receives a confirmation message: "Successfully applied a $125.00 Equipment Fee deduction for Jane Doe (ID: member_98765). The system verified the entry was accepted."

Scenario 2: Time-Off Liability Auditing

The Prompt:

"Generate a time-off balance report for all employees as of December 31st. Once it's ready, calculate the total unused hours across the company."

Agent Execution Sequence:

  1. create_a_justworks_time_off_balance_report: The agent initiates the async report, passing as_of_date: "2024-12-31". The tool returns a report_id.
  2. get_single_justworks_time_off_balance_report_by_id: The agent enters a polling loop, calling this tool with the report_id. It checks the status field. If pending, it waits.
  3. Data Extraction: Once status is ready, the agent extracts the items array.
  4. Calculation: The agent iterates through the items, looking at the available and unit_type fields. It converts any day or minute values into hours and sums the total.

Output: The Finance team receives a calculated response: "The time-off balance report generated successfully. As of Dec 31st, there is a total liability of 1,420 unused hours across all active policies."

Building Multi-Step Workflows

To build these multi-step workflows, your underlying infrastructure needs to fetch the JSON schemas from Truto, convert them into the format expected by your LLM framework, and execute a tool-calling loop.

Truto handles the schema generation dynamically based on the specific Justworks account connected. This means any custom fields configured by the specific customer are automatically injected into the tool schema.

Here is how you architect the agent execution loop using TypeScript. This example is conceptually framework-agnostic but mirrors patterns used in Vercel AI SDK and LangChain.

sequenceDiagram
    participant YourApp as Your Agent Application
    participant Truto as Truto API
    participant LLM as LLM Provider (OpenAI/Anthropic)
    participant Upstream as Justworks API

    YourApp->>Truto: GET /integrated-account/<id>/tools
    Truto-->>YourApp: Returns JSON schemas for Justworks methods
    YourApp->>LLM: Pass prompt + bound Justworks tools
    LLM-->>YourApp: Tool call requested (e.g., list_all_justworks_members)
    YourApp->>Truto: Execute tool via Proxy API
    Truto->>Upstream: Authenticated request to Justworks
    Upstream-->>Truto: 200 OK (Member Data)
    Truto-->>YourApp: Normalized JSON response
    YourApp->>LLM: Append tool result to message history
    LLM-->>YourApp: Final natural language answer

Fetching and Binding Tools

First, you retrieve the tools from Truto. You filter by methods (e.g., read, write) depending on the permissions you want to grant the agent.

// Fetching tools using standard Fetch API
async function getJustworksTools(accountId: string, trutoToken: string) {
  const response = await fetch(
    `https://api.truto.one/integrated-accounts/${accountId}/tools?methods[0]=read&methods[1]=write`,
    {
      headers: { Authorization: `Bearer ${trutoToken}` },
    }
  );
 
  if (!response.ok) {
    throw new Error(`Failed to fetch tools: ${response.statusText}`);
  }
 
  const tools = await response.json();
  return tools; 
}

Once fetched, these schemas are passed to your LLM framework. In LangChain, this looks like llm.bindTools(formattedTools).

The Execution Loop and Error Handling

When the LLM decides to call a tool, it returns a tool call object containing the function name and the generated arguments. Your system executes the call against Truto's proxy endpoint.

Crucially, this is where you must handle the API quirks discussed earlier, such as Justworks's array-based responses and rate limit headers.

async function executeJustworksTool(toolCall: any, accountId: string, trutoToken: string) {
  const maxRetries = 3;
  let attempt = 0;
 
  while (attempt < maxRetries) {
    const response = await fetch(
      `https://api.truto.one/integrated-accounts/${accountId}/proxy/${toolCall.name}`,
      {
        method: "POST", // Truto proxies tool calls via POST
        headers: { 
          Authorization: `Bearer ${trutoToken}`,
          "Content-Type": "application/json"
        },
        body: JSON.stringify(toolCall.arguments)
      }
    );
 
    // Handle Rate Limits passed directly from Justworks
    if (response.status === 429) {
      const resetTime = response.headers.get('ratelimit-reset');
      const waitSeconds = resetTime ? Math.max(1, parseInt(resetTime) - Math.floor(Date.now() / 1000)) : Math.pow(2, attempt);
      
      console.warn(`Rate limited. Waiting ${waitSeconds} seconds...`);
      await new Promise(resolve => setTimeout(resolve, waitSeconds * 1000));
      attempt++;
      continue;
    }
 
    const data = await response.json();
 
    // Trap the 201 Partial Success for Deductions
    if (toolCall.name === "create_a_justworks_deduction" || toolCall.name === "justworks_deductions_bulk_update") {
       const failures = data.items.filter((item: any) => item.success === false);
       if (failures.length > 0) {
         return JSON.stringify({
           error: "Partial failure detected in bulk operation",
           failed_items: failures
         });
       }
    }
 
    // Return successful data to the LLM context
    return JSON.stringify(data);
  }
  
  throw new Error("Max retries exceeded for Justworks API");
}

By returning the exact failure states back to the LLM as a JSON string (failed_items: failures), the agent can read the specific error_message from Justworks, recognize that its payload was invalid, and immediately attempt a correction without user intervention.

Moving from Scripts to Reliable Systems

Connecting an AI agent to an HRIS like Justworks is not just about making a REST call. It is about defending the LLM's context window from vendor-specific data models, managing complex floating-point formatting, and orchestrating secure, token-free infrastructure.

By leveraging an infrastructure layer that converts APIs into strict JSON schemas, your engineering team can focus on agent reasoning and multi-step workflow logic rather than debugging OAuth refresh loops and undocumented API behaviors.

Two ways to put Justworks to work

Elaichifrom the team behind Truto

For you and your team

Use Justworks in ChatGPT or Claude yourself

Connect Justworks once, add Elaichi to ChatGPT or Claude, and ask. Every call is checked against your own permissions and logged.

Start free, 14 days No credit card required
Truto

For product teams

Give your agent Justworks tools

Your customers connect their own Justworks accounts. Your product gets one API and MCP tools for Justworks, through Truto.

FAQ

Can AI agents safely write data to Justworks?
Yes, but write operations should be tightly scoped using specialized proxy tools with strict JSON schemas. Bulk operations like deduction updates require careful error handling, as Justworks returns a 201 status even if individual line items fail.
How do AI agents handle Justworks API rate limits?
Truto passes 429 Too Many Requests errors directly to the caller, normalizing the rate limit information into standard headers (ratelimit-limit, ratelimit-remaining, ratelimit-reset). The AI agent framework is responsible for reading these headers and executing exponential backoff.
Does Truto support LangChain and other agent frameworks?
Yes, Truto exposes Justworks operations as unified JSON schemas via the /tools endpoint, which can be natively bound to LangChain, CrewAI, Vercel AI SDK, or any other LLM framework using standard tool calling.
How are Justworks payroll amounts formatted for AI tools?
Justworks strictly separates fixed amounts and percentages. Fixed monetary amounts are formatted as integers in cents (e.g., 4500 for $45.00), while percentages require four decimal places (e.g., 37000 for 3.7%).
Justworks JustworksAI agent tools Get a sandbox

More from our Blog