ml-connector
Sage X3TaxJar

Sage X3 and TaxJar integration

Sage X3 runs accounting and inventory across your manufacturing or distribution business. TaxJar calculates sales tax in real time and automates transaction reporting to state tax authorities. Connecting the two means every time a sales invoice is completed in Sage X3, the order flows to TaxJar for tax calculation and nexus tracking, the tax liability is stored back in Sage X3, and you never manually calculate or report sales tax again. State tax filings become a simple export from TaxJar, already reconciled against what Sage X3 recorded.

How Sage X3 works

Sage X3 exposes sales invoices, GL accounts, and related line-item detail through REST api1 (HTTP Basic auth, legacy) or GraphQL with OAuth2 client credentials (recommended on V12+). Access tokens expire in 5 minutes but refresh tokens are valid 30 days. The server URL, port, and X3 folder name are customer-specific; there is no shared base URL. Sage X3 does not support webhooks or outbound event push, so invoice records are read by polling, using updatedDate fields to detect changes since the last run. On-premise deployments are the dominant model, though cloud SaaS is available through Sage-managed hosting.

How TaxJar works

TaxJar exposes tax rate lookups, transaction reporting endpoints, nexus regions, and VAT validation through a REST API at api.taxjar.com. Authentication is a single API token, sent either as Authorization: Token token="<API_TOKEN>" or Authorization: Bearer <API_TOKEN>; there is no OAuth2. Tokens are generated in the TaxJar app account settings. Sandbox tokens are available on Professional plan and above, and sandbox is stateless, validating request and response format only without persisting data. TaxJar does not publish webhooks to external endpoints; all integration is polling or merchant-initiated POST/PUT at transaction time.

What moves between them

Sales invoices flow from Sage X3 to TaxJar. When an invoice is marked completed or shipped in Sage X3, ml-connector polls for new or updated invoices, extracts the order total, shipping address, items, and customer exemption status, posts the transaction to TaxJar's /transactions endpoint for tax calculation, and stores the resulting tax liability, jurisdictions, and transaction ID back in Sage X3 as custom fields on the invoice record. Refunds and adjustments flow the same direction. No data moves from TaxJar back into Sage X3 except the calculated tax amounts and transaction receipt; nexus regions and rate lookups are read-only reference data.

How ml-connector handles it

ml-connector presents either HTTP Basic auth (for REST api1) or OAuth2 credentials (for GraphQL) to Sage X3 depending on the customer's X3 version and enabled endpoints. The API token for TaxJar is stored encrypted and sent on every call. Because Sage X3's tokens expire in 5 minutes, ml-connector refreshes proactively after each polling cycle and caches the refresh token for up to 30 days. TaxJar's transaction endpoint has an idempotency quirk: if a POST returns 422 (transaction exists), ml-connector falls back to PUT; if PUT returns 404 (not found), it retries POST. Address and exemption classification are validated in TaxJar before posting the transaction so the tax calculation is accurate. Because Sage X3 is poll-only and has no notification mechanism, ml-connector runs on a schedule you control, typically tied to your invoice approval or shipping cadence. Every transaction record carries a full audit trail, including the Sage X3 invoice ID, TaxJar transaction ID, calculated tax, and result code, so the entire flow can be replayed if a downstream posting fails.

A real-world example

A regional distributor uses Sage X3 for inventory, purchasing, and AR, and operates across four states with varying sales tax rates and nexus obligations. Before the integration, the finance team exported completed invoices from Sage X3 each week, manually entered them into TaxJar for rate lookup and nexus tracking, and tracked tax liability in a spreadsheet. Tax reporting required reconciling the spreadsheet against both systems and often revealed discrepancies that pushed deadline. With Sage X3 and TaxJar connected, each invoice automatically posts to TaxJar when marked complete in Sage X3, the tax amounts are calculated and stored on the invoice, and the monthly tax liability report is generated directly from TaxJar with full traceability back to the source invoices. The finance team spends zero time on manual tax entry and the audit trail is complete.

What you can do

  • Post completed sales invoices from Sage X3 to TaxJar for real-time tax calculation across all nexus regions.
  • Store calculated tax liability, jurisdiction breakdown, and TaxJar transaction ID back into Sage X3 invoice records.
  • Validate customer exemption status and shipping address before posting to TaxJar so tax calculations are accurate.
  • Authenticate Sage X3 with HTTP Basic auth (REST) or OAuth2 (GraphQL), and handle token refresh when Sage X3 tokens expire.
  • Poll on your invoice approval or shipping schedule, with full audit trail on every transaction so records can be replayed if a call fails.

Questions

What invoices flow from Sage X3 to TaxJar?
Completed or shipped sales invoices. ml-connector polls Sage X3 on a schedule you set, extracts the invoice total, line items, shipping address, and customer exemption status, and posts each transaction to TaxJar for tax calculation. Refunds and credit memos are handled the same way. No invoices are sent twice; ml-connector uses Sage X3's updatedDate field to detect changes since the last poll.
How does ml-connector handle Sage X3's 5-minute token expiry?
Sage X3 access tokens expire in 5 minutes, but refresh tokens are valid for 30 days. After each polling cycle, ml-connector refreshes the access token proactively and caches the refresh token. If a token refresh fails, the next cycle will re-authenticate from scratch. The stored encrypted credentials let ml-connector recover without manual intervention.
What happens if a TaxJar transaction POST fails with a 422 (exists) error?
TaxJar's transaction endpoint has an idempotency mechanism: POST returns 422 if the transaction already exists. ml-connector automatically falls back to PUT to update the record. If PUT returns 404 (not found), it retries POST. This allows the same invoice to be reprocessed without manual cleanup if a downstream step fails.

Related integrations

Connect Sage X3 and TaxJar

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

Get started