ml-connector
DATEVZoho CRM

DATEV and Zoho CRM integration

Zoho CRM runs the sales pipeline, customer records, and invoicing. DATEV runs the books. Connecting the two means a customer or an invoice raised in Zoho CRM becomes master data and a booking in DATEV without re-keying. ml-connector reads Account, Vendor, and Invoice changes from Zoho CRM, writes the matching debtor and creditor master rows and finalized journal entries into DATEV as EXTF file jobs, and uploads the invoice PDF into DATEV Unternehmen Online. Because the two systems work very differently, ml-connector handles Zoho's datacenter-routed REST API on one side and DATEV's asynchronous file-job and document APIs on the other.

How DATEV works

Zoho CRM exposes Accounts, Contacts, Vendors, Products, and customer-facing Invoices, along with Quotes, Sales Orders, and Purchase Orders, through its REST v8 API using GET, POST, PUT, PATCH, and DELETE. Authentication is OAuth 2.0 with a long-lived refresh token, and the correct base URL comes from the api_domain field in the token response because the org may sit on any of seven datacenters. Zoho can POST webhooks through its Notifications API when records change, but the payload carries only record IDs, so the full record must be fetched afterward, and notification channels expire after about a day and must be renewed. Invoice, Vendor, and Product modules require a Professional plan or higher, and invoice line items live in the Invoiced_Items subform rather than at a separate endpoint.

How Zoho CRM works

DATEV is not a conventional REST API. The Rechnungswesen accounting engine is on-premise, and finalized bookings are submitted as EXTF CSV file jobs through a cloud relay; booking suggestions and invoice data go to DATEV Unternehmen Online as DXSO XML jobs. Both are asynchronous: you submit a file, receive a job ID, and poll until it completes, because DATEV sends no webhooks. Invoice PDFs upload through the documents REST API against client-specific document types that must be fetched per client first. Authentication is OAuth 2.0 Authorization Code with PKCE through login.datev.de, which always requires a real user session and a 15-minute access token, and DATEV does not expose its chart of accounts over the API.

What moves between them

The main flow runs from Zoho CRM into DATEV. When an Invoice is created or marked sent in Zoho CRM, ml-connector reads it and builds an EXTF booking batch with the grand total, invoice number, invoice date, posting text, tax code, and cost center, then submits it to DATEV Rechnungswesen. Zoho Account changes become debtor master rows and Vendor changes become creditor master rows in DATEV. The invoice PDF attached in Zoho is uploaded into DATEV Unternehmen Online against the matching document type, typically Rechnungsausgang for outgoing invoices. Cadence is Zoho webhooks for near real-time triggering with a scheduled COQL poll as a catch-up net. DATEV bookings are write-only, so ml-connector never reads posted journal entries back into Zoho CRM.

How ml-connector handles it

ml-connector stores both credential sets encrypted and runs two unlike auth flows: Zoho's refresh-token flow, where it exchanges the refresh token at the accounts server for the org region and uses the returned api_domain as the base URL, refreshing proactively before the one-hour token expires; and DATEV's interactive Authorization Code flow with PKCE, where it refreshes the 15-minute access token using client_id only and never the secret, and sends the X-DATEV-Client-Id header on every call. The Zoho side is event-driven: it accepts the ID-only notification, verifies the echoed token string since Zoho sends no HMAC signature, then fetches the full record, and it renews the Zoho channel before its roughly one-day expiry. The DATEV side is submit-and-poll: ml-connector posts each EXTF or DXSO job, then backs off and polls the job endpoint until it clears or fails. Zoho Account and Vendor IDs map to DATEV debtor and creditor account numbers, and each Zoho record carries the DATEV cost center and SKR GL account so every line lands on a valid account, since DATEV will not return its chart of accounts. Document types are fetched per DATEV client before upload rather than hardcoded, and EXTF files are written as NFC-normalized UTF-8 with deterministic filenames so a retry re-submits cleanly instead of tripping DATEV's duplicate-file error. On a 429 from Zoho's daily credit quota it backs off and retries, and every record carries a full audit trail and can be replayed.

A real-world example

A German engineering-services firm of about 90 staff runs its sales pipeline and client billing in Zoho CRM Professional and keeps its books in DATEV through its Steuerberater. Before the integration, an administrator exported the week's invoices and new customers from Zoho and re-typed the revenue bookings and debtor records into DATEV by hand, which delayed the month-end handover to the tax advisor and left the ledger a step behind billing. With DATEV and Zoho CRM connected, each invoice raised in Zoho produces an EXTF booking in DATEV automatically, the customer becomes a debtor master record, and the invoice PDF lands in DATEV Unternehmen Online ready for the advisor. The weekly re-keying step is gone and the books track billing in near real time.

What you can do

  • Turn Zoho CRM Invoices into EXTF booking batches posted to DATEV Rechnungswesen after each invoice is raised.
  • Create DATEV debtor master rows from Zoho Accounts and creditor master rows from Zoho Vendors.
  • Upload Zoho invoice PDFs into DATEV Unternehmen Online against the correct client-specific document type.
  • Bridge Zoho's datacenter-routed refresh-token OAuth and DATEV's interactive PKCE login, refreshing each token before it expires.
  • React to Zoho webhooks, fetch the full record, then submit and poll each DATEV job with retries and a full audit trail.

Questions

Which direction does data move between DATEV and Zoho CRM?
The main flow is Zoho CRM into DATEV. Invoices, Accounts, and Vendors move from Zoho into DATEV as booking batches and debtor or creditor master records, and invoice PDFs upload into DATEV Unternehmen Online. DATEV bookings are write-only over the API, so ml-connector does not read posted journal entries back into Zoho CRM.
How does the integration handle Zoho webhooks and DATEV having no webhooks?
Zoho notifications carry only record IDs and have no HMAC signature, so ml-connector verifies the echoed token, fetches the full record, and renews the channel before its roughly one-day expiry. DATEV pushes nothing at all, so the DATEV leg is submit-and-poll: each EXTF or DXSO file job returns a job ID, and the connector backs off and polls the DATEV job endpoint until it reports complete or failed.
DATEV does not expose its chart of accounts. How do bookings land on the right account?
DATEV cannot return its standard SKR account numbers over the API, so the GL accounts and cost centers are configured in advance and carried on the Zoho records. ml-connector maps each Zoho Account to a DATEV debtor account and each Vendor to a creditor account, and stamps every EXTF line with a known GL account, cost center, and tax code so the booking posts to a valid account.

Related integrations

Connect DATEV and Zoho CRM

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

Get started