ml-connector
AcumaticaWise

Acumatica and Wise integration

Acumatica Cloud ERP runs your finance, AP, and accounting. Wise executes the international vendor payments that sit downstream of those AP bills. Connecting the two lets approved Acumatica payments leave as Wise transfers and brings the settled status, fees, and statement lines back into the ledger without re-keying. ml-connector mirrors Acumatica vendors as Wise recipients, builds the quote-and-fund sequence Wise requires, and posts each transfer outcome to Acumatica. It handles the very different auth and data models on each side and runs the sync on the cadence you set.

How Acumatica works

Acumatica Cloud ERP exposes Vendor, Bill, Payment, Account, and JournalTransaction records through its Contract-Based REST API, served as JSON over HTTPS from a tenant-specific URL whose endpoint version, such as /entity/Default/24.200.001/, must exactly match the customer's ERP release or the call returns 404. Authentication is OAuth 2.0 from the OpenID Connect identity server built into each instance, with a legacy login cookie as a fallback. All field values in request and response bodies are wrapped in value objects, and there is no native idempotency key. Acumatica can push events through Push Notifications using a shared-secret header, but the common and reliable pattern is polling the Bill and Payment entities on the LastModifiedDateTime filter with $top and $skip offset paging.

How Wise works

Wise is a payments platform, not an ERP, so it has no vendor, bill, purchase order, or GL account objects. The Wise Platform API exposes profiles, multi-currency balances, recipients, quotes, transfers, and balance statements as JSON over HTTPS. A connector authenticates with an OAuth 2.0 bearer token, whose access token expires after 12 hours, scoped to a numeric profile_id that most calls require. Creating a payment is a fixed four-step sequence: post a quote, create or reuse a recipient, create the transfer, then fund it from a balance, and skipping the funding step leaves the transfer waiting. Wise pushes transfer state changes by webhook, signed with RSA-SHA256 over the raw body.

What moves between them

The main flow runs from Acumatica into Wise. ml-connector reads approved Acumatica AP payments and their vendors, creates or reuses a matching Wise recipient, gets a quote, creates the transfer, and funds it from the chosen Wise balance. Status then flows back from Wise into Acumatica: as each transfer reaches outgoing_payment_sent or fails, the connector updates the related Acumatica payment and posts the Wise fee and any FX difference as a GL journal against the accounts you map. Balance statement lines are read for reconciliation. Wise holds no ledger, so the chart of accounts stays in Acumatica and ml-connector never writes accounting records into Wise.

How ml-connector handles it

ml-connector stores both credential sets encrypted, sends the Wise bearer token on every request and refreshes it before its 12-hour expiry, and refreshes the Acumatica OAuth token when a call returns 401. On the Acumatica side it accepts the full instance URL and the exact endpoint version per customer, since a version mismatch returns 404, and wraps every field value in the required value object so reads and writes do not fail with a 400. Vendors are mapped to Wise recipients first, and because Wise recipients are immutable, a changed vendor bank detail is handled by creating a fresh recipient rather than editing one. Each payment runs the mandatory Wise sequence, quote then recipient then transfer then fund, and the connector sets customerTransactionId from the Acumatica payment reference and a BullMQ jobId so a retried run never sends the same money twice. A fresh quote is taken if an earlier one passes its expirationTime. Wise transfer state-change webhooks arrive signed with RSA-SHA256, so each event is verified before processing and the resource is re-fetched from Wise rather than trusted from the payload; a scheduled poll on Acumatica LastModifiedDateTime and on the Wise transfer list backfills anything a webhook missed. Both sides return HTTP 429 under load, so ml-connector honors Retry-After and backs off with jitter, and every record carries a full audit trail and can be replayed if a downstream call fails.

A real-world example

A mid-sized importer of around 200 staff runs Acumatica Cloud ERP for purchasing, AP, and accounting, and pays overseas suppliers in several currencies. Before the integration, an AP clerk approved each bill in Acumatica, then rebuilt the payment by hand in the Wise dashboard, copying the IBAN and amount and guessing at the FX, after which someone keyed the Wise fee and the settled rate back into Acumatica where the bank balance rarely tied out. With Acumatica and Wise connected, an approved payment creates the Wise recipient and transfer automatically, funds it from the right currency balance, and posts the confirmed status, fee, and FX difference back to Acumatica. The double entry is gone and month-end close starts from payments that already reconcile.

What you can do

  • Turn approved Acumatica AP payments into funded Wise transfers through the required quote, recipient, transfer, and fund sequence.
  • Mirror Acumatica vendors as Wise recipients, recreating a recipient when bank details change since Wise recipients are immutable.
  • Post each Wise transfer status, processing fee, and FX difference back into Acumatica as updated payments and GL journals.
  • Authenticate Wise with its OAuth bearer token and profile id, and Acumatica with its OAuth token on the version-locked instance URL.
  • Set a customerTransactionId and jobId so a retry never double-pays, verify Wise webhook signatures, and keep a full audit trail with error replay.

Questions

Which direction does data move between Acumatica and Wise?
Mostly Acumatica into Wise, with status coming back. Approved AP payments and their vendors move from Acumatica into Wise as recipients and funded transfers. Transfer status, fees, and FX differences then flow back into Acumatica as updated payments and GL journals. Wise has no chart of accounts, so the ledger stays in Acumatica and ml-connector never writes accounting records into Wise.
How does the integration stop a retry from paying a vendor twice?
Every Wise transfer is created with a customerTransactionId derived from the Acumatica payment reference, and Wise deduplicates on that value, so a repeated request returns the original transfer instead of a new one. ml-connector also tags the job with a BullMQ jobId for its own dedup. Because Acumatica has no native idempotency key, the connector checks payment status before acting so an already-sent payment is not re-queued.
How are Wise transfer outcomes and fees reflected in Acumatica?
Wise sends transfer state-change webhooks signed with RSA-SHA256, which ml-connector verifies and then confirms by re-fetching the transfer from the Wise API. When a transfer reaches outgoing_payment_sent or fails, the linked Acumatica payment is updated, and the Wise fee and any exchange-rate difference are posted as a GL journal against the accounts you map. A scheduled poll on the Wise transfer list and on Acumatica LastModifiedDateTime backfills anything a webhook missed.

Related integrations

Connect Acumatica and Wise

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

Get started