Connect Wave to AI Agents: Automate Transactions & Billing Tasks
Learn how to connect Wave to AI agents using Truto's /tools endpoint. Bypass GraphQL complexity and automate billing workflows with LangChain or other frameworks.
You want to connect Wave to an AI agent so your system can autonomously draft invoices, record manual payments, manage customer profiles, and handle complex billing inquiries. Here is exactly how to do it using Truto's /tools endpoint and SDK, bypassing the need to build a custom GraphQL integration from scratch.
If your team uses ChatGPT, check out our guide on connecting Wave to ChatGPT, or if you are building on Anthropic's models, read our guide to connecting Wave 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 approach works with LangChain, LangGraph, CrewAI, or the Vercel AI SDK - it is not limited to standard MCP implementations, effectively solving the SaaS integration bottleneck for developers.
The accounting industry is shifting from rigid, rules-based automation to agentic AI. As modern accounting integrations evolve, finance teams expect systems that can read an inbound email from a client, verify the outstanding balance in Wave, draft a targeted invoice, and send it for approval.
Giving a Large Language Model (LLM) read and write access to a financial system of record is a massive engineering risk. You either spend weeks building, hosting, and maintaining a custom GraphQL connector, or you use an infrastructure layer that handles the boilerplate for you. For many developers, utilizing a unified accounting API is the only sustainable way to manage these connections at scale. This guide breaks down exactly how to fetch AI-ready tools for Wave, bind them natively to an LLM, and execute complex billing workflows.
The Engineering Reality of Custom Wave Connectors
Building AI agents is easy. Connecting them to external SaaS APIs safely is hard.
Giving an LLM access to external financial data sounds simple in a local prototype. You write a Node.js function that makes a fetch request to Wave and wrap it in an @tool decorator. In production, this approach collapses entirely. If you decide to build a custom integration for Wave, you own the entire API lifecycle, including OAuth token rotation, schema maintenance, and error parsing.
Wave's API introduces several specific integration challenges that break standard REST assumptions.
The LLM vs. GraphQL Disconnect
Wave utilizes a strict GraphQL API. While GraphQL is excellent for frontend applications that need to minimize payload size, it is notoriously hostile to LLMs. If you give an LLM raw access to a GraphQL endpoint, it will inevitably hallucinate the query syntax, attempt to query fields that do not exist, or fail to understand the required input hierarchy for mutations.
To make Wave accessible to an AI agent, you must translate every single GraphQL query and mutation into a discrete, REST-like function call with strict JSON schemas. Truto handles this translation layer natively. Integrations on Truto use the concept of Resources and Methods. These essentially map the underlying Wave GraphQL queries into a REST-based CRUD API. We define Proxy APIs where Truto handles the query generation and input parsing, exposing a clean, standardized tool schema to your agent.
The inputErrors Validation Trap
When a Wave GraphQL mutation fails, it rarely returns a standard HTTP 400 Bad Request. Instead, the API often returns an HTTP 200 OK response with a payload indicating didSucceed: false.
Crucially, Wave buries the actual validation failures in an inputErrors [] array. For example, if your agent tries to create an invoice without specifying a valid businessId, Wave will return an array pinpointing the missing field. If your custom integration layer does not explicitly parse this array and feed it back into the LLM's context window, the agent will assume the tool call succeeded or fail to understand why it was rejected, leading to an infinite retry loop. Truto's proxy methods automatically surface these inputErrors so the LLM can self-correct.
Transitioning to non-Classic Accounting Transactions
Wave is actively migrating its transaction architecture. When creating money transactions, the API requires specific flags, such as ensuring isClassicAccounting is false for newer mutation types like moneyTransactionCreate.
LLMs do not read vendor deprecation notices. If you hardcode a tool schema based on old API documentation, your agent will suddenly start failing when Wave enforces new transaction models. Truto abstracts this away by maintaining the tool definitions centrally. When Wave updates its schema, Truto updates the integration definition, and the /tools endpoint immediately serves the corrected schema to your agent.
Strict Rate Limits and the 429 Protocol
Wave enforces API rate limits to protect its infrastructure. If an agent goes rogue and attempts to sync thousands of historical invoices in a tight loop, Wave will respond with an HTTP 429 Too Many Requests error.
It is critical to understand Truto's architectural stance on rate limits: Truto does not retry, throttle, or apply backoff on rate limit errors. When the Wave API returns an HTTP 429, Truto passes that error directly to the caller.
However, Truto normalizes the upstream rate limit information into standardized headers per the IETF specification: ratelimit-limit, ratelimit-remaining, and ratelimit-reset. The caller (your agent execution loop) is responsible for implementing retry and exponential backoff logic. This is by design - absorbing rate limits in the proxy layer causes timeout disconnects and prevents the agent framework from accurately tracking task duration.
Fetching Wave Tools with Truto
Truto provides a dedicated /integrated-account/:id/tools endpoint that returns all available proxy APIs with their descriptions and JSON schemas, formatted specifically for LLM frameworks.
Instead of writing and maintaining fifty separate TypeScript functions for Wave, you retrieve the tools programmatically.
Here is how you initialize the tools using the Truto LangChain.js SDK:
import { TrutoToolManager } from "@trutohq/langchainjs-toolset";
import { ChatOpenAI } from "@langchain/openai";
// Initialize the LLM
const llm = new ChatOpenAI({
modelName: "gpt-4o",
temperature: 0,
});
// Initialize the Truto Tool Manager with your Wave integrated account ID
const toolManager = new TrutoToolManager({
trutoToken: process.env.TRUTO_API_KEY!,
integratedAccountId: "wave_account_id_from_truto",
});
// Fetch all available Wave tools dynamically
const waveTools = await toolManager.getTools();
// Bind the tools to the LLM
const agentWithWave = llm.bindTools(waveTools);This single block of code gives your LLM complete, schema-validated access to Wave.
Hero Tools for Wave AI Agents
While Wave has dozens of endpoints, a few high-leverage operations form the backbone of most autonomous billing workflows. Here are the most critical tools to provide to your agent.
create_a_wave_customer
This tool allows the agent to generate new customer profiles within a specific Wave business. It maps to the customerCreate mutation.
Contextual usage notes: The agent must provide a valid businessId and a name. This is usually the first step in an onboarding workflow. If the agent fails to provide required fields, it must parse the resulting inputErrors [] array to identify what went wrong.
"A new client, Stark Industries, just signed the master service agreement. Extract their corporate address from the attached PDF and create a new customer profile in our primary Wave business account."
create_a_wave_invoice
This tool executes the invoiceCreate mutation. It generates a draft invoice linked to a specific customer.
Contextual usage notes: The input requires both businessId and customerId. The agent must first query the customer or rely on previous context to obtain the correct customerId. It returns the created invoice ID, which is necessary for subsequent operations like adding line items or approving the document.
"Draft a new invoice for Stark Industries for the month of October. The total should be $5,000 for 'Retained Engineering Services'."
wave_invoices_send
Creating an invoice is useless if it sits in a draft state. This tool maps to the invoiceSend mutation and handles the actual dispatch of the document.
Contextual usage notes: The Business.emailSendEnabled flag must be true on the underlying account. The agent must supply the invoiceId and the to email addresses. It can optionally set the subject, message body, and attach the PDF.
"Review the draft invoice #1042. If the total matches our $5,000 retainer agreement, finalize it and send it to tony@starkindustries.com with a polite thank you note in the body."
create_a_wave_invoice_payment
When a client pays via check or an external wire, the agent must reconcile the outstanding balance. This tool maps to invoicePaymentCreateManual.
Contextual usage notes: This requires the invoiceId, paymentAccountId, amount, paymentDate, and paymentMethod. It is highly sensitive; agents should ideally require a human-in-the-loop approval before executing this tool to prevent false accounting records.
"We just received a wire transfer for $5,000 from Stark Industries. Record this manual payment against their outstanding October invoice. Use today's date and set the payment method to 'Bank Transfer'."
create_a_wave_estimate
For sales-led workflows, generating estimates autonomously accelerates the deal cycle. This tool maps to the estimateCreate mutation.
Contextual usage notes: Similar to invoices, this requires businessId and customerId. It returns an estimateNumber and status. If the client later accepts, the agent can use the related wave_estimates_convert_to_invoice tool to flip the document without manual data entry.
"The prospect requested a quote for our standard server migration package. Generate a Wave estimate for $12,000 and send me the PDF link so I can review it before dispatch."
To view the complete inventory of available Wave tools, schema definitions, and required parameters, visit the Wave integration page.
Workflows in Action
Exposing individual tools is only the first step. The true power of an AI agent emerges when it chains these tools together to solve multi-step accounting problems. Here are two concrete examples of how an agent navigates Wave.
Scenario 1: Autonomous Client Onboarding & Billing
Your sales team closes a deal in your CRM. A webhook triggers the agent to provision the new client in your accounting software and issue their first deposit invoice.
"A new deal for 'Wayne Enterprises' was marked Closed-Won. Create their customer profile in Wave, draft a $10,000 invoice for the initial software implementation fee, and email it to bruce@wayne.com."
Execution Steps:
get_single_wave_business_by_id: The agent fetches the default business context to ensure it has the correctbusinessId.create_a_wave_customer: The agent passesname: "Wayne Enterprises"and thebusinessId. Wave returns the newcustomerId.create_a_wave_invoice: The agent uses the newly acquiredcustomerIdand drafts the invoice for the implementation fee. Wave returns theinvoiceId.wave_invoices_send: The agent passes theinvoiceIdand the destination email, officially dispatching the bill.
Outcome: The agent autonomously bridged the gap between a sales event and revenue realization, entirely bypassing manual data entry in the Wave dashboard.
Scenario 2: Invoice Reconciliation
Your operations team receives a bulk notification from the bank regarding cleared checks. They feed the unstructured text to the agent for reconciliation.
"We received a check for $2,500 from 'Acme Corp'. Find their outstanding invoice and mark it as paid via check today."
Execution Steps:
list_all_wave_businesses: The agent verifies the operational context.get_single_wave_customer_by_id(or a generic search): The agent locates the 'Acme Corp' profile to retrieve their exact ID.update_a_wave_invoice_by_id(or querying the list): The agent identifies the outstanding $2,500 invoice.create_a_wave_invoice_payment: The agent submits the manual payment record, specifyingpaymentMethod: "Check"and linking it to the specific invoice.
Outcome: The accounting ledger is updated in real-time, preventing the finance team from accidentally sending a dunning email to a client who has already paid.
Building Multi-Step Workflows
To orchestrate these steps reliably, you need an execution loop that can handle tool calls, parse results, and manage API rate limits.
Here is a conceptual architecture using LangChain.js to execute a multi-step Wave workflow. Note how the loop handles the execution of the tools and explicitly checks for HTTP 429 status codes based on Truto's standardized headers.
import { HumanMessage, AIMessage, ToolMessage } from "@langchain/core/messages";
async function runWaveAgentLoop(llm, tools, userPrompt: string) {
const messages = [new HumanMessage(userPrompt)];
while (true) {
// 1. Agent decides what to do
const response = await llm.invoke(messages);
messages.push(response);
// 2. If no tool calls, the agent has finished the task
if (!response.tool_calls || response.tool_calls.length === 0) {
console.log("Final Answer:", response.content);
break;
}
// 3. Execute requested Wave tools
for (const toolCall of response.tool_calls) {
const selectedTool = tools.find(t => t.name === toolCall.name);
if (!selectedTool) continue;
try {
console.log(`Executing ${toolCall.name} with args:`, toolCall.args);
// Invoke the tool (this routes through Truto's Proxy API)
const toolResult = await selectedTool.invoke(toolCall.args);
// Check for specific Wave GraphQL inputErrors
if (toolResult && toolResult.inputErrors && toolResult.inputErrors.length > 0) {
console.warn("Wave Validation Failure:", toolResult.inputErrors);
// Feed errors back to the LLM so it can correct its next attempt
messages.push(new ToolMessage({
tool_call_id: toolCall.id,
content: `Failed. Validation errors: ${JSON.stringify(toolResult.inputErrors)}`Status: 200 OK
}));
continue;
}
messages.push(new ToolMessage({
tool_call_id: toolCall.id,
content: JSON.stringify(toolResult)
}));
} catch (error) {
// Handle HTTP 429 Rate Limits from Truto
if (error.response && error.response.status === 429) {
const resetTime = error.response.headers['ratelimit-reset'];
console.error(`Rate limited by Wave API. Must wait until ${resetTime}`);
// Implement your backoff logic here before retrying the tool call
// ...
}
messages.push(new ToolMessage({
tool_call_id: toolCall.id,
content: `Error executing tool: ${error.message}`
}));
}
}
}
}
// Execute the workflow
await runWaveAgentLoop(agentWithWave, waveTools,
"Create a new customer named 'Daily Planet' and draft a $500 invoice."
);By feeding errors directly back into the context window as a ToolMessage, you leverage the LLM's reasoning capabilities. If the LLM omits a required field, the Wave API rejects it, Truto passes back the precise inputErrors payload, and the LLM rewrites its request automatically on the next iteration.
The Future of Autonomous Accounting
Building integrations for AI agents requires a fundamental shift in how we handle APIs. You can no longer rely on rigid, hardcoded ETL pipelines. Agents require atomic, declarative tools that cleanly abstract the underlying complexities of vendor-specific GraphQL implementations, token management, and schema drift.
By leveraging Truto's /tools endpoint, you remove the integration bottleneck from your AI roadmap. Your engineering team can focus on refining agent logic, semantic routing, and prompt optimization, rather than spending weeks parsing Wave documentation to maintain a custom connector.
FAQ
- Does Truto automatically retry rate-limited requests to Wave?
- No. Truto does not retry, throttle, or apply backoff on rate limit errors. When Wave returns an HTTP 429, Truto passes that error directly to your caller, while normalizing the headers to standardized IETF formats (ratelimit-limit, ratelimit-remaining, ratelimit-reset). Your agent execution loop must handle the backoff.
- How does Truto handle Wave's GraphQL API for AI agents?
- Truto abstracts Wave's GraphQL mutations and queries into REST-based proxy methods. This gives the AI agent strict, atomic tool calls (like 'create_a_wave_invoice') with clear JSON schemas, preventing the LLM from hallucinating GraphQL syntax.
- What happens if an AI agent submits invalid data to Wave?
- Wave typically returns an array of inputErrors[] for validation failures. Truto surfaces these errors in the tool response, allowing the AI agent framework to read the specific missing fields and self-correct on the next iteration.
- Do I have to use LangChain to use Truto's AI tools?
- No. The Truto /tools endpoint returns standard JSON schemas that can be used with any agent framework, including LangChain, LangGraph, CrewAI, or the Vercel AI SDK.