ml-connector
Wave AccountingShipStation

Wave Accounting and ShipStation integration

Wave Accounting powers the books for small online retailers. ShipStation powers order fulfillment and carrier rates for the same merchants. Connecting the two keeps invoices in Wave aligned with orders and shipments in ShipStation, so your financial records reflect what actually shipped. When a ShipStation order ships, the invoice in Wave moves from draft to sent, and the shipment tracking updates Wave's transaction records without manual re-entry.

How Wave Accounting works

Wave Accounting exposes invoices, customers, products, accounts, transactions, vendors, and sales taxes through a GraphQL API at https://gql.waveapps.com/graphql/public, secured with OAuth 2.0 authorization code flow (2 hour token expiry, refresh tokens available with offline_access scope). Wave Pro subscription is required. Webhooks are available for invoice.created, invoice.updated, invoice.paid, payment.created, customer.created, customer.updated, transaction.created, product.created, and product.updated; return HTTP 200 to acknowledge, 500 to trigger retry. Webhook payloads are verified with HMAC-SHA256 signature (x-wave-signature header, 5 minute replay window). Invoices support created, approved, sent, or deleted states but no patch operation. Bills and purchase orders are not exposed via the API.

How ShipStation works

ShipStation exposes orders, shipments, customers, products, and warehouses through a REST API (V1 at https://ssapi.shipstation.com for order management; V2 at https://api.shipstation.com/v2 for labels and inventory). V1 authentication uses HTTP Basic Auth with base64-encoded apiKey:apiSecret; V2 uses API-Key header. Rate limits are 40 requests/min for V1 and 200 requests/min for V2. Webhooks are available for ORDER_NOTIFY, SHIP_NOTIFY, ITEM_ORDER_NOTIFY, ITEM_SHIP_NOTIFY, FULFILLMENT_SHIPPED, and FULFILLMENT_REJECTED, but payloads contain only resource_url pointers; actual data requires authenticated follow-up GET. No webhook fires on order edits; polling with modifyDate filter is required. All V1 datetime fields are in PST/PDT timezone. Orders cannot be updated once shipped or cancelled. Customers are created implicitly via orderUsername in order payloads (no direct create endpoint).

What moves between them

Orders and shipments flow from ShipStation into Wave Accounting. When a new order arrives in ShipStation, ml-connector creates a draft invoice in Wave tied to the ShipStation customer. As the order moves through fulfillment and ships in ShipStation, ml-connector transitions the Wave invoice from draft to sent and creates transaction records capturing shipment cost and tracking details. Customer data flows in both directions so Wave customers reflect ShipStation orders. Reference data such as products is synced from Wave into ShipStation so order line items match Wave's chart of accounts. The sync runs on a schedule triggered by ShipStation webhooks for shipments and by periodic polling for order changes that webhooks do not catch.

How ml-connector handles it

ml-connector stores both Wave OAuth credentials (refresh token) and ShipStation API Key encrypted per customer. It refreshes the Wave access token when it expires (every 2 hours) and uses ShipStation V1 for order and customer operations. ShipStation webhook payloads contain only resource URLs, so ml-connector fetches the full order and shipment data with an authenticated GET on arrival, extracting the customer, line items, and tracking. All ShipStation datetime values are in PST/PDT, so ml-connector converts to UTC before writing to Wave. Wave webhooks trigger invoice state transitions; when ShipStation confirms a shipment, ml-connector updates the Wave invoice to sent status. ShipStation enforces immutability on shipped and cancelled orders, so ml-connector treats those as final and does not attempt updates. The natural idempotency key for order operations is the ShipStation orderKey field, allowing safe retries. Rate limits on ShipStation V1 (40 req/min) are respected with backoff and jitter, and all records carry audit timestamps.

A real-world example

A small e-commerce business runs Wave Accounting for their general ledger and invoices, and ShipStation for multi-channel order fulfillment across Shopify, eBay, and Amazon. Before the integration, the operations team exported shipment details from ShipStation each day and manually created invoices in Wave, a task that took 30 minutes per day and often fell behind when order volume spiked. Mistakes in customer names or addresses meant follow-up corrections, and reconciling Wave invoices against actual shipments was tedious. With Wave and ShipStation connected, each order creates an invoice automatically, shipping updates flow into Wave immediately, and the finance team has an accurate, real-time view of revenue and fulfillment without re-keying.

What you can do

  • Create Wave invoices automatically when orders arrive in ShipStation, with customer and line item data populated from the order.
  • Update Wave invoice status to sent when ShipStation confirms shipment, keeping invoice state aligned with fulfillment.
  • Sync customers bidirectionally so Wave customer records reflect new ShipStation orders and ShipStation knows about Wave customers.
  • Sync products from Wave into ShipStation and map them to order line items, ensuring accounting and fulfillment use the same chart of accounts.
  • Handle Wave OAuth token refresh, ShipStation V1 rate limits, webhook pointer resolution, and PST-to-UTC timezone conversion on every record.

Questions

How does ml-connector handle ShipStation's webhook payloads that only contain resource pointers?
ShipStation webhook payloads include only a resource_url and resource_type. ml-connector immediately fetches the full order or shipment data with an authenticated GET call to that URL, extracts customer, line items, and tracking details, then creates or updates the corresponding Wave invoice. This ensures complete data without polling delays.
What happens when a Wave invoice is in draft vs sent state?
New ShipStation orders map to draft invoices in Wave, allowing review before sending. When ShipStation marks the shipment confirmed, ml-connector transitions the Wave invoice to sent status. Wave invoices support created, approved, sent, and deleted states; ml-connector respects those transitions and does not attempt edits to invoices after they are sent.
How are timezones handled between Wave (UTC) and ShipStation V1 (PST/PDT)?
All ShipStation V1 datetime fields arrive in PST/PDT. ml-connector converts them to UTC before writing to Wave Accounting, ensuring timestamps are consistent across both systems. This is critical for order sequences and audit trails that span multiple time zones.

Related integrations

Connect Wave Accounting and ShipStation

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

Get started