ml-connector
DATEVAsana

DATEV and Asana integration

DATEV is the accounting backend used by German tax advisors and their business clients. Asana runs work and approval tracking, but it holds no native finance objects. Connecting the two lets a finance approval that clears in Asana become a real booking and document in DATEV without re-keying. ml-connector reads the Asana task and its custom fields, uploads the attached invoice file to DATEV Unternehmen Online, submits the booking as an EXTF or DXSO job, and writes the DATEV outcome back onto the task. It handles the very different REST, file-based, and webhook surfaces on each side and tracks each DATEV job until it finishes posting.

How DATEV works

DATEV is not a conventional REST API. The accounting engine DATEV Rechnungswesen is on-premise, with a cloud document layer DATEV Unternehmen Online. REST products cover listing clients and uploading documents, but finalized bookings are submitted as asynchronous EXTF CSV file jobs and booking suggestions as DXSO XML jobs, on tenant client IDs that are looked up first. Authentication is OAuth 2.0 Authorization Code with PKCE against login.datev.de; a real tax advisor login is required, there is no machine-to-machine flow, and access tokens last only 900 seconds. DATEV has no webhooks, so job status is read by polling, and the standard chart of accounts cannot be read back over the API.

How Asana works

Asana exposes tasks, projects, portfolios, users, teams, goals, and customer-defined custom fields through its REST API at app.asana.com over JSON and HTTPS, with offset pagination and a default of returning only gid and name unless opt_fields is passed. Authentication is a long-lived Personal Access Token or OAuth 2.0 authorization code, sent as a bearer token. Asana has no native invoice, purchase order, payment, or GL account objects, so financial values are carried in custom fields configured per workspace. Asana pushes change events by webhook signed with HMAC-SHA256, but those webhooks auto-delete after 24 hours of failed delivery and must be re-registered.

What moves between them

The flow runs from Asana into DATEV. ml-connector reads tasks in the configured Asana project, typically approval tasks, and pulls the custom fields that hold the invoice amount, debit and credit account, document number, and cost center. The file attached to the task is uploaded to DATEV Unternehmen Online against the correct client document type, and the booking is written into DATEV as an EXTF batch or a DXSO job, then polled until it posts. The DATEV job result, posted or failed plus any DATEV error code, is written back onto the Asana task as a custom field or comment so the approver sees the outcome. Finalized DATEV bookings cannot be read back, so ml-connector never expects a confirmation read from DATEV beyond job status and never pushes ledger entries into Asana.

How ml-connector handles it

ml-connector stores both credential sets encrypted, sends the Asana bearer token in the Authorization header, and runs the DATEV interactive PKCE login once with the tax advisor, then refreshes the 900-second token automatically using the client_id alone. Because Asana returns only gid and name by default, every read passes opt_fields so the custom field values actually come back, and custom fields are resolved by GID rather than name since names can be renamed. Only tasks that have cleared the approval step are acted on, so a partly filled task is not booked early. The Asana custom field values are mapped to DATEV account numbers and cost centers first, because DATEV's chart of accounts cannot be read back and must be configured in advance, and the booking is written into the EXTF Buchungsstapel layout as UTF-8 NFC text with a whitelisted, deterministic filename so DATEV does not reject it and a retry stays safe against the duplicate filename plus document-type check. Asana has no idempotency key, so ml-connector dedupes on the task GID and a BullMQ jobId so a re-read of the same task does not double-post into DATEV. A signed Asana webhook can trigger the work as soon as a task changes, and because those webhooks auto-delete after 24 hours of failed delivery, a scheduled poll both backfills missed events and re-registers the webhook. DATEV itself has no webhooks, so each submitted job is polled with backoff until it posts or fails. Asana rate limits return HTTP 429 with a Retry-After header, so ml-connector backs off, and every record carries a full audit trail and can be replayed if a downstream call fails.

A real-world example

A German design and construction firm with roughly 120 staff keeps its books in DATEV through its Steuerberater and runs all internal approvals in Asana, including a project where incoming supplier invoices are routed for sign-off with custom fields for amount, account, and cost center. Before the integration, once an invoice task was approved in Asana the bookkeeper opened DATEV, uploaded the PDF to DATEV Unternehmen Online, and keyed the posting into the booking batch by hand, matching each line to the right SKR account and Kostenstelle. With DATEV and Asana connected, an approved task automatically uploads its attachment to DATEV Unternehmen Online and submits the booking batch allocated to the correct cost center, then stamps the DATEV result back onto the task. The manual upload and re-keying steps are gone and the approver can see in Asana whether DATEV accepted the booking.

What you can do

  • Turn approved Asana tasks into DATEV EXTF or DXSO booking jobs, then poll each job until it posts.
  • Upload the file attached to an Asana task into DATEV Unternehmen Online against the correct client document type.
  • Read Asana custom fields by GID with opt_fields and map their values to DATEV account numbers and cost centers configured in advance.
  • Authenticate Asana with a Personal Access Token or OAuth bearer token, and DATEV with interactive PKCE login plus automatic 900-second token refresh.
  • Trigger on signed Asana webhooks with a scheduled poll that backfills missed events, re-registers the auto-deleted webhook, dedupes on the task GID, and keeps a full audit trail.

Questions

Which direction does data move between DATEV and Asana?
The flow is Asana into DATEV. An approved Asana task and its custom field values become a DATEV booking job, and the task's attachment is uploaded to DATEV Unternehmen Online. The only thing written back to Asana is the DATEV job result, posted or failed, since finalized DATEV bookings cannot be read back over the API.
Asana has no invoice or GL objects, so how does this work?
Asana is a work management tool with no native invoice, purchase order, or GL account objects, so financial values are carried in customer-defined custom fields on the task. ml-connector reads those fields by GID, maps them to DATEV account numbers and cost centers configured in advance, and submits the booking, so Asana acts as the approval and intake layer while DATEV stays the system of record for the books.
How does the integration handle Asana webhooks and DATEV's lack of webhooks?
Asana pushes change events signed with HMAC-SHA256, which ml-connector verifies and uses as a trigger, but those webhooks auto-delete after 24 hours of failed delivery, so a scheduled poll both backfills missed events and re-registers the webhook. DATEV has no webhooks at all, so after a booking job is submitted ml-connector polls the DATEV job endpoint with backoff until it posts or fails.

Related integrations

Connect DATEV and Asana

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

Get started