Skip to main content
RapidDev - Software Development Agency
retool-integrationsREST API Resource

How to Integrate Retool with Wave

Connect Retool to Wave by creating a GraphQL Resource pointing to Wave's API endpoint (https://gql.waveapps.com/graphql/public). Authenticate with OAuth 2.0 and build GraphQL queries in the Retool query editor to manage invoices, customers, and products — creating a free accounting dashboard for micro-businesses that need better reporting and operational tools than Wave's native interface provides.

What you'll learn

  • How to create a Wave API application and obtain OAuth 2.0 client credentials
  • How to configure a GraphQL Resource in Retool for Wave's API endpoint
  • How to write GraphQL queries in the Retool query editor to list invoices and customers
  • How to build a financial dashboard with Wave invoice status, overdue tracking, and revenue charts
  • How to use GraphQL mutations in Retool to create and update Wave invoices from a form
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate16 min read25 minutesOtherLast updated April 2026RapidDev Engineering Team
TL;DR

Connect Retool to Wave by creating a GraphQL Resource pointing to Wave's API endpoint (https://gql.waveapps.com/graphql/public). Authenticate with OAuth 2.0 and build GraphQL queries in the Retool query editor to manage invoices, customers, and products — creating a free accounting dashboard for micro-businesses that need better reporting and operational tools than Wave's native interface provides.

Quick facts about this guide
FactValue
ToolWave
CategoryOther
MethodREST API Resource
DifficultyIntermediate
Time required25 minutes
Last updatedApril 2026

Why Connect Retool to Wave?

Wave's free accounting platform is popular with freelancers and micro-businesses, but its native reporting and operational UI has limitations — particularly for businesses that need custom financial views, invoice management across multiple clients, or integration of accounting data with other operational systems. Building a Retool Wave integration gives these businesses the operational control of a premium accounting tool's admin panel without the subscription cost.

With Wave connected to Retool, you can build an accounts receivable panel that shows all outstanding invoices sorted by overdue date, highlights invoices approaching payment deadlines, and lets finance staff send payment reminders without navigating Wave's interface for each client. You can create revenue dashboards that chart monthly invoiced amounts by customer or product, or build a customer management panel that combines Wave customer data with your own CRM notes stored in Retool Database.

Wave's GraphQL API differs from typical REST APIs — instead of fixed endpoints, you write flexible queries that request exactly the fields you need. Retool's GraphQL Resource handles this natively, providing schema introspection and auto-complete in the query editor. This means you can explore Wave's full data model (businesses, invoices, customers, products, accounts, transactions) directly in Retool and build precisely the queries your dashboard needs without consulting API documentation for every field name.

Integration method

REST API Resource

Wave uses a GraphQL API (not REST) for all data access, available at https://gql.waveapps.com/graphql/public. Retool's GraphQL Resource type connects to this endpoint, authenticating with OAuth 2.0 and auto-introspecting Wave's schema to provide type-aware query building. All requests are proxied server-side through Retool's backend. You can then write GraphQL queries and mutations in the Retool query editor to read invoices, manage customers, and access financial data — with Retool's schema explorer showing available fields and types for Wave's API.

Prerequisites

  • A Wave account at https://www.waveapps.com with at least one business configured
  • A Wave API application created at https://developer.waveapps.com/hc/en-us/sections/360001114072 with OAuth 2.0 credentials (Client ID and Client Secret)
  • The Wave API OAuth 2.0 authorization URL (https://api.waveapps.com/oauth2/authorize) and token URL (https://api.waveapps.com/oauth2/token)
  • A Retool account (Cloud or self-hosted) with permission to add Resources
  • Basic familiarity with GraphQL query syntax (queries and mutations)

Step-by-step guide

1

Create a Wave API application and obtain OAuth 2.0 credentials

Navigate to Wave's developer portal. Log into your Wave account at https://www.waveapps.com, then access the developer section through Wave's Help Center or directly at https://developer.waveapps.com. Look for the API Apps section or Applications dashboard — from here you can create a new API application. Click Create Application (or similar, depending on the current Wave developer portal UI). Fill in the required fields: - Application Name: e.g., 'Retool Dashboard' - Homepage URL: your Retool instance URL (e.g., https://yourorg.retool.com) - Redirect URI: for Retool Cloud, use https://oauth.retool.com/oauth/oauthcallback. For self-hosted Retool, use https://your-retool-instance.com/oauth/oauthcallback. Once the application is created, Wave will display your Client ID and Client Secret. Note both of these carefully. Also note the OAuth 2.0 endpoints: - Authorization URL: https://api.waveapps.com/oauth2/authorize - Token URL: https://api.waveapps.com/oauth2/token Wave's OAuth scopes for full access include: account:* (access account data), business:* (read/write business data), invoice:* (create and manage invoices), customer:* (manage customers). Check Wave's current scope documentation as available scopes may vary by plan. Store your Client ID and Client Secret — you will use them in the Retool resource configuration in the next step.

Pro tip: Wave's API uses business-level data scoping — each Wave business (account) has its own set of data. When your OAuth token is issued, it is scoped to the Wave account that authorized it. If you manage multiple Wave businesses, you may need separate OAuth tokens for each.

Expected result: You have a Wave API Client ID, Client Secret, authorization URL, and token URL ready to configure in Retool.

2

Create a GraphQL Resource in Retool for Wave

Open Retool and go to the Resources tab in the sidebar. Click Add Resource. In the resource type search, type 'GraphQL' and select GraphQL from the list. This opens the GraphQL resource configuration form — distinct from REST API, as it supports schema introspection and type-aware query building. Configure the resource: - Name: 'Wave GraphQL API' - Base URL: https://gql.waveapps.com/graphql/public (Wave's single GraphQL endpoint — all operations go to this URL) For Authentication, select 'OAuth 2.0' from the dropdown. Fill in the OAuth 2.0 fields: - Client ID: paste your Wave application's Client ID - Client Secret: paste your Wave application's Client Secret - Authorization URL: https://api.waveapps.com/oauth2/authorize - Token URL: https://api.waveapps.com/oauth2/token - Scopes: account:* business:* invoice:* customer:* (space-separated, adjust based on what Wave currently supports) - Access Token Lifespan: set appropriately based on Wave's token expiry (check your Wave app settings or API documentation) Scroll down and click Connect to OAuth (or Save and Connect). Retool will open a Wave OAuth consent screen in a popup where you log in and authorize the application. After authorization, Retool stores the access token and the resource is ready. To verify the connection works, click the 'Test Connection' button if available, or create a test query below. Click Save Changes to finalize the resource.

Pro tip: After connecting OAuth, Retool's GraphQL resource can auto-introspect Wave's schema. Click 'Introspect Schema' in the resource settings (if available) to load Wave's full data model into Retool's query editor for auto-complete. This makes building queries significantly faster.

Expected result: The Wave GraphQL API Resource appears in Retool's Resources list with an 'Authenticated' or 'Connected' status badge. The resource is ready to use in queries.

3

Build a GraphQL query to list Wave businesses and invoices

Wave's API requires you to first fetch your business ID, then use it to scope subsequent queries. Open or create a Retool app. In the Code panel, click + to create a new query. Select your Wave GraphQL API resource. The query editor for a GraphQL resource shows a text editor where you write GraphQL query syntax, and optionally a Variables section for dynamic values. Write the following query to fetch your Wave businesses: After saving and running this query, note the business ID from the response — you will use it in subsequent queries. For a production dashboard, store this as a Retool Select component that shows all businesses (in case you manage multiple), and let the user pick which business to query. Now create a second query to fetch invoices for the selected business. Create a new query in the Code panel, select the Wave GraphQL resource, and write the invoices query. Use Retool's {{ }} syntax for the variable values — Retool passes these as the GraphQL variables object automatically when the query runs. Drag a Table component onto the canvas. Set its Data to {{ invoicesQuery.data }}. Add a JavaScript transformer to the query to flatten the nested GraphQL response (GraphQL responses wrap data in a 'data' object, and Wave wraps results in edges/nodes structures for paginated collections).

waveQueries.graphql
1# Query 1: Fetch Wave businesses
2query GetBusinesses {
3 businesses {
4 edges {
5 node {
6 id
7 name
8 currency {
9 code
10 }
11 isClassicAccounting
12 isPersonal
13 }
14 }
15 }
16}
17
18# Query 2: Fetch invoices for a business
19query GetInvoices($businessId: ID!, $pageSize: Int, $status: InvoiceStatus) {
20 business(id: $businessId) {
21 invoices(
22 page: 1
23 pageSize: $pageSize
24 status: $status
25 ) {
26 pageInfo {
27 currentPage
28 totalPages
29 totalCount
30 }
31 edges {
32 node {
33 id
34 invoiceNumber
35 invoiceDate
36 dueDate
37 status
38 total {
39 value
40 currency {
41 code
42 }
43 }
44 amountDue {
45 value
46 }
47 customer {
48 id
49 name
50 }
51 }
52 }
53 }
54 }
55}

Pro tip: In Retool's GraphQL query editor, pass variables as a JSON object in the Variables section rather than hardcoding values directly in the query string. For example, set businessId to {{ businessSelect.value }} in the Variables panel — this makes the query dynamic without string interpolation in the query body.

Expected result: The businesses query returns your Wave business IDs and names. The invoices query returns a list of invoices with status, amounts, customer names, and due dates for the selected business.

4

Transform GraphQL response and build an accounts receivable view

Wave's GraphQL responses follow a nested pattern: the data is wrapped in edges/nodes arrays (a Relay-style pagination pattern). Add a transformer to each query to flatten this structure into the simple arrays that Retool's Table and Chart components expect. In your invoices query's Advanced tab, add a JavaScript transformer. Access the raw response through the 'data' variable in the transformer context. Bind the transformer output to a Table component. Set the Table's Data to {{ invoicesQuery.data }}. Configure columns: invoiceNumber, customerName, totalValue, amountDue, invoiceDate, dueDate, status, and daysOverdue. For the daysOverdue column, set conditional row coloring: when daysOverdue > 0 use red background, when daysOverdue > 14 use dark red. Set the status column to use Tag rendering with colors: PAID → green, OUTSTANDING → yellow, OVERDUE → red, DRAFT → gray. Add a revenue summary bar above the table. Use three Stat components showing: total outstanding (sum of amountDue where status OUTSTANDING), total overdue (sum where overdue), and invoiced this month (sum of totalValue where invoiceDate in current month). Bind these to JavaScript queries that aggregate invoicesQuery.data.

invoicesTransformer.js
1// Transformer to flatten Wave invoices GraphQL response
2const response = data; // raw GraphQL response
3const invoicesData = response?.business?.invoices?.edges || [];
4const today = new Date();
5return invoicesData.map(edge => {
6 const inv = edge.node;
7 const dueDate = inv.dueDate ? new Date(inv.dueDate) : null;
8 const daysOverdue = dueDate && inv.status !== 'PAID'
9 ? Math.max(0, Math.floor((today - dueDate) / (1000 * 60 * 60 * 24)))
10 : 0;
11 return {
12 id: inv.id,
13 invoiceNumber: inv.invoiceNumber || 'N/A',
14 customerName: inv.customer ? inv.customer.name : 'Unknown',
15 customerId: inv.customer ? inv.customer.id : null,
16 totalValue: inv.total ? parseFloat(inv.total.value) : 0,
17 amountDue: inv.amountDue ? parseFloat(inv.amountDue.value) : 0,
18 currency: inv.total?.currency?.code || 'USD',
19 invoiceDate: inv.invoiceDate ? new Date(inv.invoiceDate).toLocaleDateString() : 'N/A',
20 dueDate: inv.dueDate ? new Date(inv.dueDate).toLocaleDateString() : 'N/A',
21 status: inv.status || 'UNKNOWN',
22 daysOverdue: daysOverdue,
23 isOverdue: daysOverdue > 0
24 };
25});

Pro tip: For the accounts receivable view, sort the Table by daysOverdue descending by default (most overdue first) so that the invoices requiring immediate attention are always at the top without requiring manual sorting.

Expected result: A Table displays Wave invoices with customer names, amounts, due dates, and color-coded status indicators. Overdue invoices appear at the top with red highlighting. KPI stat cards show outstanding balance and overdue totals.

5

Create and update Wave invoices using GraphQL mutations

Wave's GraphQL API supports mutations for creating and updating records. To let Retool users create new invoices, build a form that collects invoice details and submits a createInvoice mutation. First, create a query to fetch Wave customers (for the customer selector in the invoice form): Create a new query in the Code panel using your Wave GraphQL resource. Write a customers query that fetches all customers for the selected business. Add a transformer that flattens the response to an array of { id, name } objects. Build the invoice form: - Drag a Container component onto the canvas - Inside it, add a Select for customer (options from customersQuery.data, label = name, value = id) - Add a Date Picker for invoice date and due date - Add a dynamic line items Table with an editable Amount and Description column (using Retool's editable Table mode) Create a mutation query in the Code panel using the Wave GraphQL resource. Set the query to a GraphQL mutation that creates an invoice. Wire the 'Create Invoice' button to trigger this mutation query. In the mutation's Success handler, re-run the invoices query to refresh the table and show a success notification with the new invoice number. For marking an invoice as paid, create a separate mutation query using Wave's patchInvoice mutation and wire it to a 'Mark Paid' button in the invoice detail panel. For complex multi-resource dashboards combining Wave with payment processors or CRM data, RapidDev's team can help architect your Retool solution.

waveMutations.graphql
1# Fetch customers for invoice form
2query GetCustomers($businessId: ID!) {
3 business(id: $businessId) {
4 customers(page: 1, pageSize: 200) {
5 edges {
6 node {
7 id
8 name
9 email
10 }
11 }
12 }
13 }
14}
15
16# Mutation: Create a new Wave invoice
17mutation CreateInvoice($input: InvoiceCreateInput!) {
18 invoiceCreate(input: $input) {
19 didSucceed
20 inputErrors {
21 code
22 message
23 field
24 }
25 invoice {
26 id
27 invoiceNumber
28 status
29 }
30 }
31}

Pro tip: Wave's GraphQL mutations use an 'input' variable pattern — construct the full input object in the Variables section of your Retool query, referencing form component values with {{ }} expressions. This keeps the mutation query body clean and static while the variables carry the dynamic form data.

Expected result: The invoice creation form allows selecting a customer, setting dates, and adding line items. Submitting the form creates a new invoice in Wave via the GraphQL mutation. The invoice table refreshes and shows the new invoice with DRAFT status.

Common use cases

Build an accounts receivable tracking dashboard

Create a Retool app that queries Wave for all outstanding invoices across your business, displaying client name, invoice amount, due date, days overdue, and payment status. Overdue invoices are highlighted in red using conditional row coloring. Finance managers can click an invoice to view its line items and mark it as paid by triggering a Wave mutation from a button — updating the invoice status without leaving Retool.

Retool Prompt

Build an accounts receivable panel that queries Wave's invoices GraphQL endpoint for all unpaid and overdue invoices, displays them in a Table with columns for customer_name, invoice_number, amount_due, due_date, days_overdue, and status. Highlight rows where days_overdue > 0 in red. Add a 'Mark Paid' button that calls a Wave GraphQL mutation to update the invoice status.

Copy this prompt to try it in Retool

Monthly revenue and customer billing dashboard

Build a Retool financial dashboard that fetches Wave invoice data for the past 12 months and visualizes monthly revenue trends with a line chart. A table shows top customers by total invoiced amount and average payment time. The dashboard provides the revenue analytics that Wave's native reports only show as static PDFs, enabling real-time monitoring and custom date range analysis directly in Retool.

Retool Prompt

Build a revenue dashboard that queries Wave invoices for the last 12 months via GraphQL, groups them by month and customer in a JavaScript transformer, and displays a line Chart of monthly_revenue over time. Add a Table showing top 10 customers by total_invoiced and average_days_to_pay. Include KPI stat cards for this_month_revenue, outstanding_balance, and overdue_amount.

Copy this prompt to try it in Retool

Invoice creation panel for operational teams

Create a Retool form-based app that lets operations team members create Wave invoices directly from Retool without Wave account access. The form pulls the customer list from Wave, lets the user add line items with product descriptions and amounts, and submits a GraphQL mutation to create the invoice in Wave. This makes invoice creation accessible to sales or project managers who should not have full Wave admin access.

Retool Prompt

Build an invoice creation form that loads Wave customers from the GraphQL API into a Select dropdown. Add a dynamic line items table where users can add product descriptions, quantities, and unit prices. A 'Create Invoice' button constructs and submits a Wave createInvoice GraphQL mutation with the form data. On success, show the new invoice number and a link to view it in Wave.

Copy this prompt to try it in Retool

Troubleshooting

OAuth 2.0 connection fails with 'invalid_client' or 'redirect_uri_mismatch' error

Cause: The redirect URI registered in your Wave API application does not match the OAuth callback URL that Retool uses. Wave's OAuth requires an exact match between registered and requested redirect URIs.

Solution: In your Wave developer application settings, verify the redirect URI is set to exactly https://oauth.retool.com/oauth/oauthcallback (for Retool Cloud) or https://your-retool-domain.com/oauth/oauthcallback (for self-hosted). The URLs must match character-for-character including trailing slashes. Update the Wave application if needed, then retry the OAuth connection in Retool.

GraphQL queries return null for invoice or customer data even after successful authentication

Cause: Wave organizes data under a specific business ID. Queries that omit the business ID or use an incorrect one return null for nested business data. Each Wave account may have multiple businesses, and the query must target the correct one.

Solution: First run the GetBusinesses query to confirm your business ID and ensure your OAuth token has access to it. Then verify that your invoice and customer queries are passing the correct businessId variable. In the Retool query Variables section, explicitly set businessId to the correct ID value from the businesses query response.

GraphQL mutations fail with 'didSucceed: false' and inputErrors

Cause: Wave's mutation responses use a pattern where didSucceed indicates success and inputErrors contains field-level validation errors when the mutation fails. The mutation query itself succeeds (HTTP 200) but the business logic validation failed.

Solution: Inspect the inputErrors array in the mutation response for specific error codes and messages. Common issues: missing required fields in the invoice input object (like businessId not included in the input), customer ID not found in the specified business, or date format mismatches. Add error handling in your Retool query's transformer to surface inputErrors to a notification when didSucceed is false.

typescript
1// Check mutation success in a post-query transformer
2const result = data.invoiceCreate;
3if (!result.didSucceed) {
4 const errors = result.inputErrors.map(e => e.message).join(', ');
5 throw new Error('Wave mutation failed: ' + errors);
6}
7return result.invoice;

Schema introspection returns no types or an empty schema in the GraphQL resource

Cause: Wave's GraphQL endpoint may require the Authorization header to be present even for introspection requests. If the OAuth token is not attached during the introspection call, the endpoint returns an unauthorized or empty schema.

Solution: Ensure the OAuth connection is fully established before attempting schema introspection (the resource shows a 'Connected' status). If introspection still fails, manually reference field names from Wave's developer documentation (https://developer.waveapps.com/hc/en-us/articles/360019968212) rather than relying on auto-complete. The queries still work correctly even without schema introspection.

Best practices

  • Always run the businesses query first to obtain your Wave business ID and store it in a Retool Select component or configuration variable — all subsequent Wave queries must scope their data to a specific business ID.
  • Use GraphQL variables (the Variables section in Retool's query editor) rather than string interpolation in query bodies — this prevents GraphQL syntax errors and allows Retool to properly type-check the variable values.
  • Add a transformer to every Wave query that checks for GraphQL errors in the response root (response.errors array) and surfaces them as Retool notifications, since GraphQL responses return HTTP 200 even when errors occur.
  • Cache your customers and businesses queries with a long cache duration (30-60 minutes) since these datasets change infrequently — this reduces API calls on every form interaction without meaningful staleness risk.
  • Use Wave's invoice status filter in your query (status: OUTSTANDING or status: OVERDUE) to limit the data fetched to only what the dashboard needs, rather than fetching all invoices and filtering client-side.
  • For the accounts receivable view, create a dedicated query that fetches only overdue invoices (filter by status=OVERDUE) and bind it to a prominently placed urgent-action panel at the top of the dashboard.
  • Store Wave configuration values (business IDs, default currency codes) in Retool configuration variables (Settings → Configuration Variables) so they can be updated by admins without modifying queries.

Alternatives

Frequently asked questions

Is Wave's API free to use, or does it require a paid plan?

Wave's API is available to Wave users at no additional cost — Wave's core accounting features (invoicing, expense tracking, reporting) are free, and API access is included. You do need to create a developer application in Wave's developer portal to obtain OAuth 2.0 credentials. Wave's optional paid services (payroll, payments) are separate from API access.

Why does Wave use GraphQL instead of a REST API?

Wave chose GraphQL to allow API consumers to request exactly the data they need in a single query, avoiding over-fetching (receiving unnecessary fields) or under-fetching (requiring multiple requests to assemble related data). In Retool, this means you can query invoices together with their line items, customer details, and payment history in a single query rather than making multiple REST calls and joining the results.

Can I manage Wave invoices for multiple businesses from a single Retool dashboard?

Yes. First run the businesses query to fetch all businesses associated with your Wave account, then build a business selector dropdown in Retool. Each subsequent query passes the selected business ID as a variable. Since OAuth tokens are scoped to your Wave account (which may contain multiple businesses), a single OAuth connection handles all businesses your account manages.

Does the Retool Wave integration support Wave Payments (collecting card payments)?

Wave Payments is a separate paid service integrated with Wave's invoicing. Via the GraphQL API, you can read the payment status of invoices and see payment method details, but initiating new payment link generation or processing payments programmatically requires Wave's Payments API capabilities — check Wave's current developer documentation for payment mutation support, as this varies by Wave's API version.

How do I handle Wave's paginated GraphQL responses for large invoice lists?

Wave's GraphQL collections use page-based pagination with page and pageSize arguments plus a pageInfo object in the response containing currentPage, totalPages, and totalCount. In Retool, add pagination variables to your query (passing page as {{ pagination.page || 1 }} and pageSize as {{ pagination.pageSize || 50 }}) and bind a Retool Table's pagination controls to these variables. The Table will automatically trigger re-runs as the user navigates pages.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Integrations are where projects stall

We wire Retool integrations like this one every week — auth, webhooks, edge cases included. Fixed-price builds from $13K, delivered in 6–10 weeks.

Talk to an integration engineer

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.