DATEV and BILL integration
DATEV is the accounting and tax backend German firms keep their books in. BILL runs accounts payable and receivable, holding the bills, vendors, payments, and invoices a business works on day to day. Connecting the two means approved bills and the payments made against them post into DATEV as bookings, with each supporting invoice PDF filed in DATEV Unternehmen Online, all without re-keying. ml-connector handles the very different APIs on each side and submits the data on a schedule you control. Because DATEV cannot return posted journal entries through its API, BILL stays the working surface and DATEV remains the system of record.
What moves between them
The flow runs from BILL into DATEV. ml-connector reads approved bills and their line items, recorded payments, and the vendor records behind them from BILL, then submits them to DATEV as EXTF CSV booking batches for Rechnungswesen or DXSO XML jobs for Unternehmen Online, mapped to the matching DATEV GL accounts, tax codes, and cost centers. The invoice PDF attached to each bill is uploaded to Unternehmen Online as a Rechnungseingang document so the booking has its source receipt. Because DATEV cannot return posted journals and its chart of accounts is not readable, the mapping is one-way: ml-connector never writes ledger entries back into BILL, and account and tax-code references are configured up front rather than pulled from DATEV.
How ml-connector handles it
ml-connector stores both credential sets encrypted. On the BILL side it logs in with the developer key and organization credentials to get a sessionId, re-authenticating whenever a call returns 401 since BILL gives no explicit expiry code, and it keeps in-flight requests under BILL's limit of three concurrent calls per org. On the DATEV side it holds the OAuth2 refresh token the tax advisor granted and renews the 15-minute access token before each batch, sending only the client_id on refresh as DATEV requires, and it keeps the state value at least 20 characters on the initial consent. BILL bill.created and payment.updated webhooks, verified by HMAC-SHA256 over the minified body, act as triggers, with a scheduled poll backfilling anything a webhook missed because BILL disables a subscription after repeated delivery failures. GL accounts, tax codes, and cost centers are mapped first, since DATEV will not return its chart of accounts, so every EXTF or DXSO line lands on a valid account. Each booking job is submitted, then polled with exponential backoff until DATEV reports complete or failed, because there is no synchronous confirmation. EXTF files use stable deterministic filenames and NFC-normalized UTF-8 so a retry is duplicate-safe rather than rejected with DATEV error DCO01253. Every record carries a full audit trail and can be replayed if a downstream call fails.
A real-world example
A 60-person facilities services company in Germany runs its books with a DATEV tax advisor but manages day-to-day accounts payable in BILL, where managers approve vendor bills and schedule payments. Before the integration, a bookkeeper exported approved bills each week and re-typed them into a DATEV EXTF file by hand, matched each one to a GL account and tax code, and emailed scanned invoices to the advisor separately, so postings lagged and the advisor often chased missing receipts at month-end. With DATEV and BILL connected, each approved bill and recorded payment is submitted to DATEV as a booking job within the polling window, the invoice PDF is filed in Unternehmen Online against the same entry, and the line is allocated to the correct account and cost center. The weekly re-keying disappears and the advisor closes the period with the receipts already attached.
What you can do
- Submit approved BILL bills and recorded payments into DATEV as EXTF CSV or DXSO XML booking jobs.
- Upload each BILL invoice PDF to DATEV Unternehmen Online as a Rechnungseingang document tied to the booking.
- Map BILL line items to DATEV GL accounts, tax codes, and cost centers configured up front, since DATEV will not return its chart of accounts.
- Bridge BILL session-token login and the DATEV OAuth2 PKCE consent the tax advisor grants, refreshing each token before it expires.
- Trigger on BILL webhooks, poll each DATEV job to confirm posting, and replay any failed record from a full audit trail.
Questions
- Which direction does data move between DATEV and BILL?
- The flow is one-way, from BILL into DATEV. Approved bills, recorded payments, vendor data, and invoice PDFs move from BILL into DATEV as booking jobs and document uploads. DATEV cannot return posted journal entries through its API and its chart of accounts is not readable, so ml-connector never writes ledger data back into BILL and account references are configured in advance.
- How does the integration handle DATEV's lack of webhooks and asynchronous bookings?
- DATEV sends no webhooks and processes bookings as asynchronous file jobs, so there is no instant confirmation that an entry posted. ml-connector submits each EXTF or DXSO job, receives a job ID, and polls the status endpoint with exponential backoff until DATEV reports complete or failed. On the BILL side it uses bill.created and payment.updated webhooks as triggers, with a scheduled poll to backfill anything a webhook missed.
- How are the two different logins reconciled?
- BILL uses a session token from a developer-key login that expires after about 35 minutes of inactivity, while DATEV uses OAuth2 Authorization Code with PKCE that the tax advisor must consent to interactively, with a 15-minute access token. ml-connector stores both encrypted, re-logs into BILL whenever a call returns 401, and refreshes the DATEV token before each batch using the client_id only, as DATEV requires on refresh.
Related integrations
More DATEV integrations
Other systems that connect to BILL
Connect DATEV and BILL
Free to use. Add your credentials, ping your real systems, and see if we fit.
Get started