ml-connector
Wave AccountingSnowflake

Wave Accounting and Snowflake integration

Wave Accounting powers invoicing and expense tracking for small businesses. Snowflake is where you centralize financial data for analysis and reporting. Connecting the two moves your Wave invoices, customers, and transactions into Snowflake tables automatically, so your finance team can query invoice history, payment status, and transaction details without manual exports. Wave webhooks trigger the sync in near-real time, and ml-connector handles the OAuth token refresh and the schema mapping from Wave's GraphQL model to your Snowflake tables.

How Wave Accounting works

Wave Accounting exposes invoices, customers, products, accounts, transactions, and sales taxes through a GraphQL single-endpoint API at https://gql.waveapps.com/graphql/public. Authentication uses OAuth 2.0 Authorization Code Flow with access tokens (2 hour expiry) and refresh tokens, and requires the connected business to hold an active Wave Pro subscription. Wave publishes webhooks for invoice.created, invoice.updated, invoice.paid, payment.created, customer.created, customer.updated, transaction.created, product.created, and product.updated, retrying failed deliveries until the endpoint returns HTTP 200. Webhook signature verification uses HMAC-SHA256 with a 5 minute replay window. Invoices can be created, approved, sent, or deleted but not patched in-place. Bills and accounts payable are not exposed via the GraphQL API.

How Snowflake works

Snowflake is a cloud data warehouse that serves as the storage and query engine for financial records. Access is via REST API (SQL API and resource-specific endpoints) and requires authentication with either Key Pair Authentication (RSA private key plus JWT, recommended for server-to-server use) or Programmatic Access Tokens (long-lived bearer tokens). Snowflake has no built-in finance objects, so all entities such as invoices, customers, and GL accounts are user-defined tables. Change detection relies on polling via timestamp watermark queries (SELECT WHERE updated_at > last_sync), native Streams for CDC, or scheduled Tasks. The SQL API supports partition-based pagination and gzip compression, and async queries return HTTP 202 with a statement handle for polling. Warehouse auto-resume must be enabled. Network policies require that connector egress IPs be whitelisted. Rate limiting returns HTTP 429 on overages.

What moves between them

Invoice and customer records flow from Wave into Snowflake. When a Wave webhook fires (invoice.created, invoice.updated, customer.created, customer.updated, or payment.created), ml-connector pulls the full record from Wave via GraphQL, transforms it to match your Snowflake schema, and inserts or updates the row in the target table. Transactions and product data can also be synced on a schedule for reconciliation queries. The sync is one-way: Snowflake never writes back to Wave. Wave webhooks drive the cadence in near-real time, backed up by periodic polling to catch any missed events.

How ml-connector handles it

ml-connector stores the Wave OAuth refresh token encrypted and refreshes the access token when the 2 hour expiry approaches or when a GraphQL call fails with a 401. For Snowflake, ml-connector uses Key Pair Authentication (RSA private key plus JWT) to authenticate REST API calls, avoiding the need for long-lived bearer tokens and allowing automatic token generation on each call. The integration subscribes to Wave webhooks for invoices, customers, and payments, validates each webhook signature using HMAC-SHA256, and posts the record to a Snowflake table via REST. If the Snowflake insert fails (network timeout or transient error), ml-connector retries with exponential backoff and logs the failure in an audit table so the record can be replayed. The schema mapping is documented in your Snowflake database as a mapping table (Wave field name to Snowflake column), so analysts can trace where each column came from. Products and accounts are synced on a schedule (e.g., daily) via GraphQL polling queries, and the integration maintains a watermark timestamp to skip records already synced.

A real-world example

A small business accounting firm manages invoicing for a dozen client companies using Wave Accounting, and uses Snowflake to consolidate financial data for cross-client analytics and month-end reporting. Before the integration, the firm exported CSV reports from each client's Wave account monthly and manually loaded them into Snowflake, a process that took 8 hours and introduced transcription errors. With Wave and Snowflake connected, every invoice and payment syncs to Snowflake automatically on creation or change, and the firm's analysts can run real-time queries on invoice aging, payment status, and customer totals across all clients without re-entry. Month-end close accelerates because the invoice and transaction tables are already current.

What you can do

  • Sync Wave invoices and customers to Snowflake tables in near-real time via webhooks, with full audit trail and replay on failure.
  • Store Wave OAuth tokens encrypted and refresh access tokens automatically so tokens never expire during a sync.
  • Authenticate to Snowflake using Key Pair Authentication (RSA private key plus JWT) for secure server-to-server access without long-lived bearer tokens.
  • Validate Wave webhook signatures using HMAC-SHA256 to ensure data integrity and protect against replay attacks.
  • Query invoice history, payment status, and transaction details directly in Snowflake using standard SQL on tables that stay in sync with Wave Accounting.

Questions

Which direction does data move between Wave and Snowflake?
Data flows one way, from Wave into Snowflake. Invoices, customers, products, transactions, and payment records sync into Snowflake tables via webhooks and polling. Snowflake is read-only for analytics and reporting; ml-connector never writes data back to Wave.
Does Wave's webhook signature verification add complexity?
No. ml-connector validates the HMAC-SHA256 signature on every incoming webhook using the x-wave-signature header and your registered webhook secret, and rejects any signature that fails or is older than 5 minutes. This happens automatically on each event, protecting against replay attacks and tampering.
How does the integration handle Wave OAuth token expiry?
ml-connector stores the Wave refresh token encrypted and monitors the access token expiry (2 hours). Before the token expires or when a GraphQL call returns 401, ml-connector calls the Wave OAuth endpoint to obtain a fresh access token. This happens transparently, so long-running syncs never encounter auth failures due to token expiry.

Related integrations

Connect Wave Accounting and Snowflake

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

Get started