ml-connector
DATEVServiceTitan

DATEV and ServiceTitan integration

DATEV runs German accounting and tax. ServiceTitan runs dispatch, invoicing, and payments for field-service businesses. Connecting the two moves each day's billed work into the books without re-keying. ml-connector reads ServiceTitan invoices, customer payments, and purchase-order-derived AP bills, then submits them to DATEV as EXTF booking batches and uploads the supporting documents to DATEV Unternehmen Online. Because DATEV processes imports asynchronously and never pushes events, ml-connector polls each job to confirm the booking posted.

How DATEV works

DATEV is not a conventional REST system. Finalized bookings go to the on-premise DATEV Rechnungswesen engine as EXTF CSV batch files (Buchungsstapel), with fields such as Konto, Gegenkonto, the Soll/Haben debit-credit indicator, Belegfeld 1, Buchungstext, Steuerschluessel, and Kostenstelle. Booking suggestions and invoice data go to DATEV Unternehmen Online as DXSO XML jobs, and invoice PDFs upload through a documents REST API using a client-specific document type. Authentication is OAuth 2.0 Authorization Code with PKCE that a tax advisor or client must approve interactively; the access token lasts 900 seconds. There are no webhooks, so every import is submitted as an async job and polled until it completes. The standard chart of accounts cannot be read back through the API, and finalized EXTF bookings are write-only.

How ServiceTitan works

ServiceTitan exposes its finance data through a REST v2 API where the tenant ID is part of every URL path. It reads and writes invoices and payments, and reads inventory bills, which are the AP bills ServiceTitan generates when a purchase order is received. Customers, vendors, tax zones, and business units are available, with the business unit acting as the cost-center dimension. Authentication is OAuth 2.0 client credentials, a machine-to-machine flow that also requires an ST-App-Key header on every call, and the token is valid for 900 seconds. ServiceTitan can push invoice, payment, and job events by webhook signed with HMAC-SHA256, and it has no chart-of-accounts endpoint, so GL mapping happens in the receiving ERP.

What moves between them

The flow runs from ServiceTitan into DATEV. ml-connector reads ServiceTitan invoices, customer payments, and inventory (AP) bills and writes them into DATEV: revenue and tax from invoices and the offsetting payment entries post as EXTF booking batches to DATEV Rechnungswesen, while invoice and bill PDFs upload to DATEV Unternehmen Online. Each ServiceTitan business unit maps to a DATEV Kostenstelle so every line lands on the right cost center, and tax zones map to DATEV tax codes. DATEV finalized bookings are write-only and the chart of accounts is not readable, so ml-connector never reads journals back into ServiceTitan; it syncs on a schedule, or triggers immediately when a ServiceTitan invoice or payment webhook arrives.

How ml-connector handles it

ml-connector stores both credential sets encrypted. On the ServiceTitan side it caches the client-credentials token for its full 15-minute life, sends the ST-App-Key header on every call, and keeps the tenant ID in each URL path. On the DATEV side it holds the refresh token from the tax advisor's PKCE approval and renews the 15-minute access token by sending the client_id only, never the client secret. ServiceTitan invoices, payments, and inventory bills are translated into EXTF CSV rows with the correct Konto, Gegenkonto, Soll/Haben indicator, Steuerschluessel, and Kostenstelle, then submitted as an import job. Because DATEV has no webhooks, ml-connector polls each job with backing-off retries until it reports complete or failed. EXTF files are written as NFC-precomposed UTF-8 with deterministic filenames, so a retried submission reuses the same name and DATEV's duplicate-by-filename check prevents a double posting. Document types are fetched per DATEV client before any upload rather than hardcoded, business units and tax zones are mapped first so no booking references a missing cost center or tax code, and every record carries a full audit trail and can be replayed.

A real-world example

A regional HVAC and plumbing contractor in Germany runs ServiceTitan for dispatch, invoicing, and payments across about 120 field technicians, and its accounting is kept in DATEV by an external tax advisory firm. Before the integration, an office administrator exported invoices and payments from ServiceTitan each week and re-typed the totals into a DATEV import file by hand, splitting them across branches and chasing tax-code errors that the advisor flagged at month end. With DATEV and ServiceTitan connected, each invoice and payment posts into DATEV as an EXTF booking batch allocated to the branch's cost center, the supporting PDFs land in DATEV Unternehmen Online, and the advisor starts the period with clean, categorized entries instead of a re-keying backlog.

What you can do

  • Post ServiceTitan invoices and customer payments into DATEV Rechnungswesen as EXTF booking batches.
  • Map each ServiceTitan business unit to a DATEV Kostenstelle and each tax zone to a DATEV tax code.
  • Upload ServiceTitan invoice and AP bill PDFs into DATEV Unternehmen Online using the correct client-specific document type.
  • Bridge ServiceTitan client-credentials tokens and the DATEV PKCE login a tax advisor approves, refreshing each 15-minute token automatically.
  • Poll every async DATEV import job to confirm the booking posted, with deterministic filenames so retries never double-post.

Questions

Which direction does data move between DATEV and ServiceTitan?
Data moves from ServiceTitan into DATEV. ServiceTitan invoices, payments, and purchase-order-derived AP bills are read and written into DATEV as EXTF booking batches and document uploads. DATEV finalized bookings are write-only and its chart of accounts cannot be read through the API, so ml-connector does not read journals back into ServiceTitan.
How does the integration handle DATEV having no webhooks?
DATEV processes every booking and document import as an asynchronous job and never pushes notifications. After ml-connector submits an EXTF or document job, it polls the job status endpoint with backing-off retries until DATEV reports complete or failed. On the ServiceTitan side, invoice and payment webhooks can trigger a sync immediately, and a scheduled run backfills anything a webhook missed.
How are cost centers and tax codes kept correct in DATEV?
ServiceTitan business units act as its cost-center dimension and its tax zones define tax rates, so ml-connector maps each business unit to a DATEV Kostenstelle and each tax zone to a DATEV Steuerschluessel before any booking is sent. Because DATEV cannot return its chart of accounts, the GL account and cost-center mappings are configured up front so every EXTF line references values that already exist in the client's DATEV books.

Related integrations

Connect DATEV and ServiceTitan

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

Get started