ml-connector
Sage 100Zuora

Sage 100 and Zuora integration

Sage 100 runs accounts payable and the general ledger for your business. Zuora manages subscriptions and billing. Connecting them ensures your GL reflects the accruals and cash from each subscription invoice and payment without manual journal entry. New vendor records in Sage 100 are synced to Zuora so invoices route to the correct cost center, and each billing event from Zuora generates a corresponding entry in your GL so revenue recognition stays current.

How Sage 100 works

Sage 100 is an on-premises Windows-based ERP system covering accounts receivable, accounts payable, general ledger, inventory, purchasing, and sales. It exposes data through eBusiness Web Services (SOAP) for basic AR and sales orders, and through Business Object Interface (BOI), a COM layer wrapped by a local Windows agent, for full access to AP invoices, GL accounts, GL journals, vendors, and purchase orders. Authentication is stateless username and password per SOAP call, or Windows service account credentials plus agent-layer API key or mutual TLS for BOI. Sage 100 has no webhooks, so all reads are by polling using DateCreated or DateLastUpdated fields.

How Zuora works

Zuora is a cloud subscription billing platform exposing accounts, subscriptions, invoices, payments, and refunds through a REST API authenticated with OAuth 2.0 client credentials. Tokens expire in one hour and must be reused across requests. The base URL is tenant-specific and varies by region (rest.zuora.com, rest.eu.zuora.com, rest.ap.zuora.com). Zuora publishes billing events through webhook Callout Notifications (invoice posted, payment processed, subscription renewed) as HTTPS POST to a registered endpoint with HMAC-SHA256 signature for verification. Payment webhooks fire only for electronic payments, not manual or external payments. The webhook payload is minimal (typically just the object ID), so subscribers must fetch the full record from Zuora.

What moves between them

The integration runs in both directions. From Sage 100 into Zuora: GL accounts and vendors are polled hourly and synced to Zuora so billing journal entries can allocate correctly. From Zuora into Sage 100: invoice posted, payment processed, and refund/credit memo events arrive via webhook, and ml-connector posts the corresponding deferred revenue, cash receivable, and revenue recognition entries into Sage 100's general ledger, mapped to the GL accounts synced from Sage 100. The cadence is real-time for webhook events (invoice and payment) and hourly polling for reference data (GL accounts and vendors).

How ml-connector handles it

ml-connector maintains two credential sets: Sage 100 agent credentials (Windows service account plus agent API key or mutual TLS) routed to the customer's on-premises BOI agent, and Zuora OAuth client_id and client_secret. For Sage 100, it polls GL_Account and AP_Vendor on an hourly schedule, checking DateLastUpdated to fetch only changed records. For Zuora, it registers as a Callout endpoint, verifies each webhook signature with HMAC-SHA256 using the registered Zuora API key, and parses the notification to determine the record type (invoice, payment, subscription event). When a payment webhook arrives, ml-connector fetches the full payment record from Zuora to extract the invoice reference and amount, then constructs a cash receivable journal in Sage 100 with debit to cash, credit to a deferred revenue control account, mapped to the GL accounts synced earlier. Rate limits are handled: Zuora enforces 50,000 requests per minute in production; ml-connector queues Zuora calls and retries with backoff on 429. Sage 100's on-premises agent enforces concurrent write limits due to COM record locking, so ml-connector batches GL journal posts and retries individually failed entries. Every webhook and poll result is logged with source ID and timestamp for audit and replay.

A real-world example

A software-as-a-service company runs Sage 100 on premises for AP and GL, and Zuora in the cloud for subscription billing and invoicing. The finance team previously exported invoice summaries from Zuora each day and manually created revenue and cash receivable journals in Sage 100, with month-end reconciliation revealing gaps between Zuora's invoice and payment records and Sage 100's GL balances. With Sage 100 and Zuora connected, each invoice posted in Zuora triggers a deferred revenue entry in Sage 100's GL, and each electronic payment from a customer posts a cash receivable journal matched to the invoice. Vendor and GL account master records stay in sync without re-keying, and the finance team gains real-time visibility into subscription revenue and cash collections in the GL.

What you can do

  • Poll Sage 100 GL accounts and AP vendors hourly and sync them to Zuora reference records so subscription invoices allocate to the correct cost center.
  • Receive Zuora invoice posted, payment processed, and refund webhooks and post the corresponding deferred revenue and cash receivable entries into Sage 100 GL.
  • Verify each incoming Zuora webhook with HMAC-SHA256 signature authentication to ensure the event is authentic.
  • Handle Zuora OAuth 2.0 token refresh, Sage 100 on-premises agent connectivity, and rate limit backoff with a full audit trail on each record.
  • Replay failed GL journal posts if a Zuora webhook is received out of order or a Sage 100 write temporarily times out.

Questions

How does ml-connector handle the mismatch between Zuora webhooks (event notifications) and Sage 100's polling-only architecture?
ml-connector registers as a Zuora Callout endpoint to receive webhooks in real-time for invoices and payments, then immediately pushes the corresponding GL entries into Sage 100's general ledger via the BOI agent. Sage 100 GL accounts and vendors are polled hourly to ensure reference data stays current. This hybrid approach gives Zuora near-real-time GL impact while respecting Sage 100's on-premises constraint that there is no outbound notification.
What do I need to configure on my Sage 100 server for this integration to work?
You must deploy a local Windows service (the BOI agent) on your Sage 100 server or an adjacent machine on the same network that can reach the Sage 100 COM layer. The service needs a Windows domain account with permissions to read and write AR, AP, and GL records in Sage 100, and it must be configured with an API key or mutual TLS certificate for authentication to ml-connector. Your Sage 100 administrator must also enable Web Services access for the user account in Sage 100's security settings.
Are GL journals posted to Sage 100 idempotent if a Zuora webhook is delivered twice?
Yes. ml-connector stores each Zuora webhook ID and GL journal cross-reference, so if a duplicate invoice posted or payment processed webhook arrives, it checks for an existing journal and skips the post. Each GL journal carries a reference ID (Zuora invoice number or payment ID) and timestamp, enabling audit and manual replay if a post fails partway through the GL account write.

Related integrations

Connect Sage 100 and Zuora

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

Get started