ml-connector
Wave AccountingFishbowl

Wave Accounting and Fishbowl integration

Wave Accounting tracks your invoicing and revenue. Fishbowl Advanced manages your inventory and orders. Connecting the two keeps your sales orders and customer records in sync without re-entry. When you create a customer or invoice in Wave, Fishbowl reflects that immediately so you can pick and ship against the right accounts. ml-connector bridges Wave's cloud GraphQL API and Fishbowl's on-premise REST API, handling the very different authentication models and the fact that Fishbowl runs in your own data center.

How Wave Accounting works

Wave Accounting exposes invoices, customers, products, accounts, transactions, and vendors through a GraphQL single-endpoint API at https://gql.waveapps.com/graphql/public. It uses OAuth 2.0 with access tokens valid for 2 hours and refresh tokens that require the offline_access scope. Wave publishes webhooks for invoice creation, updates, payment, customer events, and transaction creation; webhooks use HMAC-SHA256 signatures in the x-wave-signature header and expire if not acknowledged within 5 minutes. The connected Wave account must have an active Pro subscription. Invoices can be created, approved, sent, or deleted, but not patched; purchase orders and payroll are not accessible through the API.

How Fishbowl works

Fishbowl Advanced runs on-premise at a customer-supplied server URL and port (typically 2456) and exposes vendors, purchase orders, sales orders, parts, inventory, customers, and manufacturing orders through HTTP REST. Authentication is session-based: POST /api/login with username, password, appName, appDescription, and optional MFA code returns a UUID token used as a Bearer token in subsequent requests. Fishbowl has no outbound webhooks; all data is read through polling endpoints like GET /api/purchase-orders with date filters or GET /api/data-query with SQL. General ledger accounts are not exposed; they are managed through QuickBooks or Xero integration.

What moves between them

The main flow moves from Wave into Fishbowl. Invoices and customer records created or updated in Wave are read via GraphQL, mapped to Fishbowl customers and sales orders, and written into Fishbowl via REST. Products are synchronized in both directions so invoice line items reference inventory that already exists in Fishbowl. Because Fishbowl is pull-only and has no webhook support, ml-connector polls Wave's GraphQL endpoints on a schedule you define (typically daily or per-invoice-event). Wave webhooks are also accepted where enabled to accelerate the sync.

How ml-connector handles it

ml-connector stores Wave OAuth credentials encrypted and refreshes the access token automatically when it expires (every 2 hours). On the Fishbowl side, it accepts the customer-supplied server URL and port, authenticates once per session, and maintains a persistent Bearer token across calls. Wave webhook payloads are validated by computing the HMAC-SHA256 signature of the request body against the stored secret and checking the x-wave-signature header; a mismatch returns 401. When writing to Fishbowl, ml-connector maps Wave customers and products to matching Fishbowl entities by name or SKU; if a customer does not exist in Fishbowl, it creates one. Invoice-to-sales-order mapping is defined per customer so the same Wave invoice can route to different Fishbowl workflows depending on the account. Because Fishbowl is on-premise, ml-connector never assumes a shared base URL and always uses the customer-supplied endpoint. Retries respect Fishbowl's polling window (recommended 5 to 15 minute intervals) and track the most recent sync timestamp so no record is processed twice.

A real-world example

A small manufacturing business uses Wave Accounting to invoice customers and track revenue. Their sales fulfillment happens in Fishbowl Advanced running on a local server. Before the integration, when an order came in, the sales team created it in Wave, then logged into Fishbowl separately to create a matching sales order so warehouse could pick and ship. Invoices and customer records often went out of sync, and the team manually reconciled the mismatch during month-end close. With Wave and Fishbowl connected, each customer and invoice created in Wave automatically populates in Fishbowl at the next sync pulse (triggered by a webhook or a scheduled poll), so the warehouse always sees current customer details and current orders. The team no longer re-enters orders, and month-end reconciliation is simpler because both systems start from the same source.

What you can do

  • Sync Wave invoices into Fishbowl as sales orders, mapped to the correct customer.
  • Keep Fishbowl customers in sync with Wave customer records, including name, email, and billing address.
  • Map Wave products to Fishbowl parts so invoice line items reference inventory that exists in Fishbowl.
  • Handle Wave's OAuth 2.0 refresh cycle and Fishbowl's on-premise session authentication transparently.
  • Accept Wave webhooks to accelerate the sync, and poll on a schedule you define if webhooks are unavailable.

Questions

How does the integration handle Wave and Fishbowl's different data models?
Wave tracks invoices and customers; Fishbowl tracks sales orders and inventory. ml-connector maps Wave invoices to Fishbowl sales orders and Wave customers to Fishbowl customers. Products are matched by name or SKU so invoice line items reference inventory that already exists in Fishbowl. If a customer or product does not exist in Fishbowl, ml-connector creates it.
What happens when Wave webhooks are not enabled or fail?
ml-connector polls Wave's GraphQL API on a schedule you define, typically once per day or more frequently if you choose. The polling interval is independent of Fishbowl's recommended 5 to 15 minute fetch window; ml-connector tracks the most recent sync timestamp to avoid duplicate processing.
Why does Fishbowl require a customer-supplied server URL instead of a fixed endpoint?
Fishbowl Advanced is an on-premise system, not a SaaS platform. Each customer runs their own Fishbowl instance on their own server at a URL and port they control. ml-connector stores the customer-supplied server URL and uses it for all REST calls, so there is no shared base address like there is with Wave's hosted GraphQL endpoint.

Related integrations

Connect Wave Accounting and Fishbowl

Free to use. Add your credentials, ping your real systems, and see if we fit.

Get started