ml-connector
Oracle NetSuiteBILL

Oracle NetSuite and BILL integration

Oracle NetSuite is the accounting system of record for vendors, bills, and the general ledger. BILL runs the accounts payable workflow that pays those bills by check, ACH, or virtual card. Connecting the two keeps vendor master data in agreement and moves approved bills out of NetSuite into BILL for payment without re-keying. When BILL completes a payment, the status and payment date flow back so the source vendor bill reflects what was actually paid. ml-connector handles the very different auth on each side and moves the data on a schedule you control.

How Oracle NetSuite works

Oracle NetSuite exposes vendors, vendor bills, purchase orders, vendor payments, GL accounts, and accounting dimensions through its SuiteTalk REST Web Services on an account-specific host such as accountId.suitetalk.api.netsuite.com. Bulk and filtered reads run through SuiteQL rather than slow collection GETs. Auth is OAuth 2.0 client credentials, which signs a JWT with an RSA or EC private key whose public certificate is uploaded to the account in advance, and tokens last 60 minutes with no refresh token. NetSuite can push create and edit events through Event Subscriptions, but those events carry no HMAC signature, so reads are best done by scheduled SuiteQL polling.

How BILL works

BILL exposes vendors, bills, AP payments, customers, invoices, and the chart of accounts through its v3 REST API at gateway.prod.bill.com/connect. Authentication is a session token: a login call with a developer key, organization ID, email, and password returns a sessionId passed on every request, and that session expires after 35 minutes of inactivity. Concurrency is capped at three in-flight calls per organization and pages return up to 100 records. BILL has no purchase order object, since purchase orders stay in the ERP and arrive in BILL as bills. BILL can also push bill and payment webhooks signed with HMAC-SHA256.

What moves between them

The main flow runs from Oracle NetSuite into BILL. Vendor records sync first so every bill has a payee, then approved NetSuite vendor bills are created as bills in BILL for payment, with each line mapped to the matching GL account. Purchase orders stay in NetSuite, since BILL has no PO object. Payment status runs the other direction: when BILL pays a bill, ml-connector reads the payment and posts a vendor payment or status update onto the source NetSuite vendor bill. Reads from NetSuite use scheduled SuiteQL polling, and BILL payment changes can be picked up by webhook or poll on the cadence you set.

How ml-connector handles it

ml-connector stores both credential sets encrypted and signs a fresh JWT against the NetSuite token endpoint when the 60-minute access token expires, while logging in to BILL for a sessionId and re-running that login whenever a call returns 401, because BILL gives no clear session-expired code. It accepts the full NetSuite account-specific host per customer, since there is no shared base URL, and the subsidiary ID where the account uses OneWorld, so transaction writes do not 400. GL accounts and dimensions are mapped first so every bill line references an account that exists on both sides. When NetSuite vendor bills are created in BILL, ml-connector records the returned BILL bill ID and checks it before re-creating, because BILL has no idempotency key, and it keeps BILL traffic under the three-concurrent-call limit with backoff on the BDC rate-limit codes. On the NetSuite side it uses externalId upsert so a replayed payment write does not duplicate, and inbound BILL webhooks are verified against the x-bill-sha-signature HMAC before processing. Every record carries a full audit trail and can be replayed if a downstream call fails.

A real-world example

A mid-sized professional services firm with about 200 employees runs Oracle NetSuite for its general ledger and vendor records and uses BILL to route bill approvals and send vendor payments. Before the integration, an AP clerk re-keyed every approved NetSuite bill into BILL to schedule the payment, then later typed the cleared payment date and method back into NetSuite from BILL, and month-end close stalled while the team chased bills that were paid in BILL but still open in the ledger. With Oracle NetSuite and BILL connected, approved bills flow into BILL for payment automatically against the right GL accounts, and completed payments post back onto the source vendor bills. The double entry is gone and the AP subledger ties out at close.

What you can do

  • Sync Oracle NetSuite vendors into BILL so every bill has a matching payee before payment.
  • Create approved NetSuite vendor bills as bills in BILL for payment by check, ACH, or virtual card.
  • Read completed BILL payments and post payment status and dates back onto the source NetSuite vendor bills.
  • Map GL accounts and dimensions on both sides so each bill line lands on a valid account.
  • Bridge NetSuite signed-JWT auth and the short-lived BILL session login, refreshing each on expiry.

Questions

Which direction does data move between Oracle NetSuite and BILL?
Vendors and approved vendor bills move from Oracle NetSuite into BILL so payments run there. Payment status moves the other way, from BILL back onto the source NetSuite vendor bill once a payment completes. Purchase orders stay in NetSuite, because BILL has no purchase order object.
How does the integration handle the two different authentication methods?
Oracle NetSuite uses OAuth 2.0 client credentials, where ml-connector signs a JWT with the customer's private key and gets a 60-minute token with no refresh. BILL uses a session token from a developer-key login that expires after about 35 minutes of inactivity. ml-connector stores both credential sets encrypted, signs a new NetSuite JWT when the token expires, and re-logs in to BILL automatically whenever a call returns a 401.
How are duplicate bills and payments prevented?
BILL has no native idempotency key, so ml-connector records the BILL bill ID returned on creation and checks it before creating the same bill again. On the NetSuite side it writes payment updates using externalId upsert, so a replayed write updates the existing record instead of duplicating it. Inbound BILL webhooks are also verified against the x-bill-sha-signature HMAC before they are processed.

Related integrations

Connect Oracle NetSuite and BILL

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

Get started