ml-connector
QADExpensify

QAD and Expensify integration

QAD runs manufacturing, procurement, and finance. Expensify runs employee expense management, reporting, and corporate card reconciliation. Connecting the two moves approved employee spend into the general ledger without re-keying. Each approved or reimbursed Expensify report posts into QAD as a supplier invoice or GL expense journal, coded to the GL accounts and cost centers that QAD owns. ml-connector handles the very different APIs on each side and moves the data on a schedule you control.

How QAD works

QAD Adaptive ERP exposes suppliers, purchase orders, supplier invoices, GL accounts, cost centers, items, goods receipts, and AP payments through REST business document APIs, documented in Swagger inside each customer instance. The cloud product authenticates with a JWT session or OAuth2 bearer token against a tenant-specific URL, so there is no shared hostname. Older on-premise sites run QAD Enterprise Edition with the QXtend SOAP framework instead. QAD has no public webhook system for cloud connectors, so finance records are read by polling.

How Expensify works

Expensify exposes expense reports, individual expenses, employees, and policies through its Integration Server API. Every operation is an HTTP POST to a single endpoint, and the type field in the JSON body selects export, create, or update. Authentication is a partnerUserID and partnerUserSecret pair sent inline in every request, with no Authorization header, no tokens, and no scopes. Amounts are returned in cents, GL codes attach to categories through a categoryGLCodeMap, and tags act as accounting dimensions. Expensify does not push events, has no cursor pagination, and offers no sandbox, so exports are filtered by date and an exported flag and run against production.

What moves between them

The main flow runs from Expensify into QAD. ml-connector exports approved and reimbursable expense reports from Expensify and posts them into QAD as supplier invoices or GL expense journals, with each expense line coded by mapping its Expensify category to a QAD GL account and its tag to a QAD cost center. Reference data moves the other direction so the GL accounts and cost centers QAD owns drive the category GL codes and tags configured on each Expensify policy, and employee records sync into Expensify policies so submitters and approvers stay current. Expensify has no native invoice, purchase order, or payment object, so the integration treats the approved report as the AP claim rather than expecting Expensify to hold those entities.

How ml-connector handles it

ml-connector stores both credential sets encrypted, sends the Expensify partnerUserID and partnerUserSecret inline in the requestJobDescription on every call, and accepts the full QAD tenant URL per customer since QAD publishes no shared base address. Because neither system pushes events, it polls Expensify on a schedule, filtering with approvedAfter and a stored last-processed timestamp to pull only new reports, then calls the markAsExported onReceive action so the same report is never ingested twice. Each amount arrives in cents and is converted before it posts to QAD. Categories map to QAD GL accounts and tags map to QAD cost centers, so a report only posts when every line resolves to an account and cost center that already exists in QAD. Expensify enforces five requests per ten seconds and twenty per sixty seconds and returns HTTP 429 with no Retry-After header, so ml-connector keeps the poll well under the limit and applies exponential backoff with jitter on a 429. Because there is no idempotency key, the exported flag plus an externalID on any created expense prevent duplicates. Every record carries a full audit trail and can be replayed if a downstream call fails.

A real-world example

A mid-sized discrete manufacturer with roughly 250 employees runs QAD Adaptive ERP for production, procurement, and finance, and uses Expensify Classic for employee expenses and corporate card reconciliation across two plants. Before the integration, the finance team exported approved expense reports from Expensify each week and re-keyed the totals into QAD by hand, guessing at the right GL account and cost center, which slowed month-end close and left expense coding inconsistent. With QAD and Expensify connected, each approved report posts into QAD automatically as a supplier invoice or GL journal, coded by category to the right account and by tag to the plant cost center, and the report is flagged exported so it never posts twice. The re-keying step is gone and employee spend reaches the ledger coded the same way every period.

What you can do

  • Post approved and reimbursable Expensify expense reports into QAD as supplier invoices or GL expense journals.
  • Map each Expensify category to a QAD GL account and each tag to a QAD cost center so every line is coded correctly.
  • Drive Expensify policy category GL codes and tags from the QAD chart of accounts and cost centers.
  • Authenticate Expensify with its inline partner credential pair and QAD with its tenant-specific token.
  • Poll on a schedule with the exported flag to prevent duplicates, backoff on 429, and a full audit trail on every record.

Questions

Which direction does data move between QAD and Expensify?
The main flow is Expensify into QAD. Approved and reimbursable expense reports move from Expensify into QAD as supplier invoices or GL expense journals, while reference data moves the other way so QAD GL accounts and cost centers drive the category GL codes and tags on each Expensify policy. Employee records sync into Expensify policies so submitters and approvers stay current.
Can Expensify send AP invoices or payments to QAD?
No. Expensify is an expense management tool and has no native invoice, purchase order, or payment object in its API. The approved expense report is treated as the AP claim, and ml-connector posts it into QAD as a supplier invoice or GL journal coded by category and tag. Vendor names appear only as the free-text merchant field, so they are not synced as a QAD supplier master.
How does the integration avoid duplicates without webhooks or idempotency keys?
Expensify does not push events and documents no idempotency key, so ml-connector polls on a schedule and filters with approvedAfter and a stored last-processed timestamp to pull only new reports. After a successful ingest it calls the markAsExported onReceive action so the report is skipped on the next cycle, and any created expense carries an externalID for tracking. Amounts arrive in cents and are converted before posting to QAD.

Related integrations

Connect QAD and Expensify

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

Get started