ml-connector
Sage X3Expensify

Sage X3 and Expensify integration

Sage X3 runs your finance and operations. Expensify collects and approves employee expenses. Connecting the two flows approved expense reports into Sage X3's general ledger without manual entry or re-keying. Each expense line posts to the GL account and cost center you specify for its category, so labor allocations, travel, and project costs land on the right accounts in the ledger. ml-connector handles the polling, authentication, and audit trail so your month-end close starts with expenses already posted.

How Sage X3 works

Expensify exposes expense reports, individual expenses, employees, and report approval status through a single REST endpoint that accepts POST requests. All calls go to https://integrations.expensify.com/Integration-Server/ExpensifyIntegrations with a JSON payload that includes the API key pair (partnerUserID and partnerUserSecret). Expensify has no webhooks, so records are retrieved by polling with date-range and status filters. Expense reports are the primary entity; each contains a list of individual transactions with merchant names, amounts, categories, and approval state. Expensify has no vendor master, no invoice entities, and no GL account master - GL account codes are properties of the expense categories you configure within your Expensify workspace.

How Expensify works

Sage X3 exposes general ledger accounts, cost centers, journal entries, and customers through REST api1 (legacy) or GraphQL (Xtrem) endpoints. REST api1 uses HTTP Basic Authentication or a legacy path; Xtrem GraphQL uses OAuth2 client credentials via a Connected Application and is recommended for new integrations. Access tokens expire in five minutes. Sage X3 does not support outbound webhooks, so integrations must poll. Records are detected by checking the updatedDate or modifiedDateTime field. The X3 server URL, port, and application folder are customer-specific; there is no central base URL. On-premise is the dominant deployment, though Sage-managed cloud is available. Analytical dimensions on GL entries vary by customer configuration.

What moves between them

Expensify expense reports flow into Sage X3. ml-connector polls Expensify on a schedule you define, typically daily or weekly depending on your approval cadence. For each approved report, it creates a GL journal entry in Sage X3 with one line per expense, mapped to the GL account and cost center specified in your Expensify category configuration. The posting date matches the expense date. Expensify is read-only to ml-connector; no data flows back to Expensify. Cost centers and GL accounts must already exist in Sage X3 for the integration to succeed.

How ml-connector handles it

ml-connector stores the Expensify API key pair encrypted and submits POST requests to the Expensify integration endpoint with date-range and status filters to retrieve approved reports. For Sage X3, it uses either OAuth2 client credentials for GraphQL (Xtrem) or HTTP Basic Authentication for REST api1, authenticated against your customer-specific X3 server URL. After retrieving each report, ml-connector transforms the expenses into GL journal line items, mapping each expense category to a pre-configured GL account and cost center in X3. If the GL account or cost center does not exist, the posting fails and is logged in the audit trail. ml-connector polls Expensify on a configurable schedule rather than waiting for a push, and it retries failed posts up to a configurable limit with exponential backoff. Every transaction carries a full audit trail, including the original Expensify report ID and the GL entry ID in Sage X3, so a failed post can be investigated and replayed.

A real-world example

A mid-sized professional services company runs Sage X3 on-premise for finance and project accounting, and Expensify for employee travel, meals, and project expenses. Before the integration, the finance team downloaded approved expense reports from Expensify weekly, mapped each transaction manually to the right cost center and GL account based on the project code in the expense note, and then entered the batch into Sage X3. Managers had to chase down missing receipts and approval holds that delayed month-end close. With Sage X3 and Expensify connected, each approved report posts automatically to the ledger on the day of approval, allocated to the correct project and cost center. The finance team runs a weekly reconciliation check in ml-connector's audit log instead, and month-end close begins with all expenses already in the ledger.

What you can do

  • Post approved Expensify expense reports into Sage X3 as GL journal entries, allocated to the correct GL accounts and cost centers based on expense category.
  • Poll Expensify on a schedule tied to your approval workflow, and retry failed postings with automatic logging.
  • Map Expensify categories to Sage X3 GL accounts and cost centers so each expense line lands on the right account.
  • Authenticate Expensify with API key pairs and Sage X3 with OAuth2 or HTTP Basic auth against your customer-specific server URL.
  • Maintain a full audit trail of every posted expense report, with Expensify report ID, GL entry ID, and any posting errors for reconciliation.

Questions

Which direction does data move between Expensify and Sage X3?
Data flows one direction: from Expensify into Sage X3. Approved expense reports are read from Expensify and posted as GL journal entries into Sage X3. Sage X3 GL accounts and cost centers are referenced but not written to by the integration. No data flows back to Expensify.
What happens if a Sage X3 GL account or cost center does not exist?
The posting fails and is recorded in the audit trail with the reason (GL account not found or cost center not found). ml-connector does not create GL accounts or cost centers; they must be configured in Sage X3 before the integration can post. A failed entry can be corrected and replayed once the missing dimension is added.
How does ml-connector handle Sage X3 and Expensify authentication?
For Expensify, ml-connector stores the API key pair encrypted and submits POST requests to Expensify's integration endpoint with the credentials. For Sage X3, it authenticates with either OAuth2 client credentials (for GraphQL on X3 v12+) or HTTP Basic Authentication (for REST api1), against your customer-specific X3 server URL and port. Tokens and credentials are cached and refreshed as needed.

Related integrations

Connect Sage X3 and Expensify

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

Get started