ml-connector
SAP Business OneServiceTitan

SAP Business One and ServiceTitan integration

SAP Business One runs finance and operations for field-service businesses. ServiceTitan runs dispatch, scheduling, and invoicing for HVAC, plumbing, electrical, and other trades. Connecting the two keeps your project costs and customer billing synchronized across both systems. Invoices and payments posted in ServiceTitan flow into SAP Business One's general ledger and AR accounts without manual re-entry, and customer records stay in agreement. ml-connector bridges the gap between SAP's on-premise architecture and ServiceTitan's cloud API.

How SAP Business One works

SAP Business One exposes invoices, payments, journal entries, chart of accounts, business partners (customers and vendors), items, purchase orders, and inventory through OData v4 REST at the customer's own Service Layer instance (https://<server>:<port>/b1s/v2/). Authentication uses session tokens via POST /Login with a B1SESSION cookie that expires after 30 minutes of inactivity, or OAuth2 / OIDC if the customer has IAM configured (v10.0 FP 2305+). The system is on-premise only with no global SaaS endpoint. Webhooks for Create/Update/Delete events are supported in v10.0 FP 2602+ if Webhook Messenger Service is activated, but polling with UpdateDate filters is the standard approach since on-premise instances cannot reliably reach outbound endpoints.

How ServiceTitan works

ServiceTitan exposes invoices, payments, inventory bills, purchase orders, vendors, customers, items, employees, jobs, and appointments through REST at https://api.servicetitan.io/{namespace}/v2/tenant/{tenantId}/ (production) or the integration sandbox. Authentication uses OAuth2 client-credentials grant requiring Client ID, Client Secret, App Key (sent as ST-App-Key header), and Tenant ID in the URL path. Tokens are valid for 900 seconds. ServiceTitan supports outbound webhooks with HMAC-SHA256 signature verification for events like invoice.created, invoice.updated, payment.created, and customer.created, with automatic retries at 10s, 30s, 60s, and 300s intervals. The API is rate-limited to 60 calls per second per app per tenant. AP Bills are read-only and generated automatically when purchase orders are marked received. Chart of accounts is not exposed via the ServiceTitan API.

What moves between them

The main flow is ServiceTitan into SAP Business One. Invoices and payments created or updated in ServiceTitan are read on a scheduled basis and posted into SAP Business One's general ledger and accounts-receivable accounts, mapped to the correct customer (business partner) in SAP. Customer records in ServiceTitan are also synced to SAP Business One business partners so headcount and contract data stays aligned. The sync runs on a daily or weekly schedule configurable by the customer. SAP Business One data is read-only to ServiceTitan; no GL entries flow back from SAP to ServiceTitan.

How ml-connector handles it

ml-connector manages OAuth2 token refresh on the ServiceTitan side, caching tokens to avoid requesting a new one per call and automatically requesting a fresh token if a call returns 401. For SAP Business One, it handles session creation and reuse, waiting out the approximately 5-second cold-start on initial login and then using the session for all subsequent calls within the 30-minute timeout window before requesting a new session. Since ServiceTitan is cloud and SAP Business One is on-premise, there is no inbound push from SAP; instead, ml-connector polls SAP on a schedule to detect new or updated invoices and payments using OData filter parameters like UpdateDate. Customer must provide the full Service Layer base URL and port for their SAP instance. Before posting any journal entries into SAP, ml-connector maps ServiceTitan invoice line items to SAP chart-of-accounts entries and business partner references. The ServiceTitan App Key header is sent on every call. If SAP returns an OData error (for example, a business partner does not exist), ml-connector records the failure in the audit trail and retries with exponential backoff. Because SAP has no documented idempotency key scheme, ml-connector checks for duplicate DocNum entries before posting to prevent double-entry.

A real-world example

A mid-sized HVAC contractor runs ServiceTitan for field dispatch and job invoicing across 15 service technicians, and SAP Business One for accounting and inventory management. Before the integration, the accounting team spent three hours each week exporting invoices from ServiceTitan, manually categorizing them by job type, and posting them as batch entries into SAP's general ledger, then manually adding customer payment details to accounts receivable. With ServiceTitan and SAP Business One connected, each invoice posted in the field automatically lands in SAP's AR account under the correct customer and project code, payment records sync in real-time, and the weekly manual reconciliation is gone. Month-end accounting closes two days faster, and technicians no longer hear about invoicing discrepancies because the two systems are always in sync.

What you can do

  • Automatically post ServiceTitan invoices into SAP Business One's accounts receivable and general ledger with correct customer and line-item GL account mapping.
  • Sync ServiceTitan payment records into SAP Business One's incoming-payment ledger and cash accounts, maintaining full audit trail for every transaction.
  • Keep customer records (business partners) in sync between ServiceTitan and SAP Business One so field and accounting operations reference the same customer master.
  • Handle on-premise SAP session authentication with automatic token refresh and reuse, plus OAuth2 on the ServiceTitan side, managing both auth models transparently.
  • Poll SAP Business One on a configurable schedule since it is on-premise and cannot push, with full retry, deduplication, and audit trail on every record.

Questions

How does ml-connector handle the different auth models between on-premise SAP and cloud ServiceTitan?
ml-connector manages SAP Business One session tokens on the on-premise side, creating a login session once and reusing it for up to 30 minutes before requesting a new one. On the ServiceTitan side it uses OAuth2 client-credentials grant, requesting a token once per 900-second window and caching it to avoid repeated auth calls. The ST-App-Key header is sent on every ServiceTitan request as required by their API.
Why does SAP Business One data flow only one direction (ServiceTitan to SAP) and not the other way?
ServiceTitan is a field-service platform with no GL account exposure in its API, so there is no target for SAP's chart-of-accounts or journal entries to post back to. The main value is syncing billable work (invoices and payments) from the field into SAP's financials. Customer master data can flow both directions to keep both systems current.
What happens if a customer record does not exist in SAP when ml-connector tries to post a ServiceTitan invoice?
ml-connector checks for the business partner in SAP before posting the invoice line. If the customer does not exist, the record is marked as failed in the audit trail with details on which customer was missing, and ml-connector retries with exponential backoff. The customer must add the missing business partner in SAP or ServiceTitan to resolve the block.

Related integrations

Connect SAP Business One and ServiceTitan

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

Get started