Skip to content

Request an integration

Tell us which system you want to connect. We review every request weekly and publish a step-by-step guide or build the connection.

DATEV payroll export (LODAS / Lohn und Gehalt)

Produce DATEV LODAS or Lohn und Gehalt movement data (Bewegungsdaten) from approved TimeTracking hours, with an export mapping plus a small post-processing step.

Last updated: · Markdown version

1. Summary

What syncs Approved hours per hour type (Lohnart), allowances, approved expenses, approved leave
Direction TimeTracking → DATEV LODAS or DATEV Lohn und Gehalt (usually via the tax adviser / Steuerberater)
Mechanism Export mapping (format: "txt", ;, decimal comma) + post-processing (ANSI encoding, LODAS header block) → ASCII import
Trigger Monthly payroll

Status: Recipe — works today with a custom mapping and a post-processing script. No native DATEV connector, and TimeTracking cannot yet write the DATEV header block or ANSI encoding by itself.

2. Mechanism

  1. Use the German rule pack de-basic (ArbZG breaks, 11 h rest, daily overtime after 8 h → overtime_125, night 23:00–06:00 → night, Sunday → sunday, public holidays → holiday) or your own pack aligned with the Tarifvertrag.
  2. A custom export mapping writes one line per employee per Lohnart per month.
  3. The committed file is downloaded with GET /exports/runs/{id}/file, which delivers a windows-1252 mapping as Windows-1252 (ANSI). For LODAS, a small post-processor prepends the required header sections.
  4. The tax adviser or payroll office imports the file in LODAS or Lohn und Gehalt (ASCII import of Bewegungsdaten).

Assumption — DATEV LODAS ASCII import files consist of sections such as [Allgemein] (Ziel, Version, Berater-/Mandantennummer, Abrechnungszeitraum), [Satzbeschreibung] (record layout definition) and [Bewegungsdaten] (data lines), in ANSI encoding with ; separators and decimal comma. Lohn und Gehalt uses a similar ASCII import with a different record definition. The exact record numbers and field lists must be agreed with the tax adviser; verify with your DATEV consultant / Steuerberater.

3. Prerequisites

  • TimeTracking admin; API key with write scope.
  • Org country DE (public holidays) and rule pack de-basic or a custom pack.
  • employee_number = DATEV Personalnummer (numeric, up to 5 digits in many setups — assumption, verify).
  • DATEV Beraternummer, Mandantennummer and the Lohnart numbers per hour type.

4. Entity mapping

DATEV field TimeTracking Notes / external ID
Beraternummer / Mandantennummer Not in TimeTracking Header block (post-processing).
Abrechnungszeitraum Export range Header block, e.g. 01/2027.
Personalnummer employee_number → employeeNumber Only stable key today.
Lohnart hourType via map normal, overtime_125, night, sunday, holiday, leave_*, expense_*.
Stunden minutes → hours_decimal with ,
Betrag amountCents → cents_decimal with , Expenses, allowances.
Kostenstelle placementRef or clientName Optional.
Tage (days) Not available PayLine has no day count.

External IDs: upsert people, clients and placements idempotently with PUT /{people|clients|placements}/by-external/{source}/{ref} (or POST /…/batch with externalSource + externalRef per row, up to 500 rows). The response includes external_source, external_ref and created: true|false. Use for example source datev with the Personalnummer as ref.

5. Step-by-step setup

export TT=https://app.timetracking.tech/api/v1
export KEY=tt_live_…        # write scope
  1. Select rule pack de-basic in Regels & koppelingen (admin).

  2. Create the mapping yourself (admin, dashboard): open the export mappings screen (/export/mappings, linked from Export), copy preset-generic or preset-sdworx, edit columns, code tables and validation, and optionally set it as the org default. Saved mappings are validated; max 50 per org. The mapping id is the UUID in its URL (/export/mappings/<uuid>): pass it as mappingId to POST /exports and tt export --mapping. Example JSON for the editor (assumed layout — verify with your DATEV / Steuerberater consultant):

{
  "id": "datev-lodas",
  "name": "DATEV LODAS — Bewegungsdaten",
  "format": "txt",
  "delimiter": ";",
  "includeHeader": false,
  "encoding": "windows-1252",
  "lineEnding": "crlf",
  "groupBy": ["employeeNumber", "hourType"],
  "columns": [
    { "header": "Personalnummer", "source": "employeeNumber" },
    { "header": "Lohnart", "source": "hourType", "transform": { "kind": "map", "table": "lohnart" } },
    { "header": "Stunden", "source": "minutes", "transform": { "kind": "hours_decimal", "decimals": 2, "decimalSeparator": "," } },
    { "header": "Betrag", "source": "amountCents", "transform": { "kind": "cents_decimal", "decimalSeparator": "," } },
    { "header": "Kostenstelle", "source": "placementRef" }
  ],
  "tables": { "lohnart": { "normal": "100", "overtime_125": "200", "night": "300", "sunday": "310", "holiday": "320",
                           "leave_vacation": "400", "leave_sick": "410", "expense_km": "500" } },
  "validation": [
    { "column": "Personalnummer", "required": true, "pattern": "^\\d{1,5}$" },
    { "column": "Lohnart", "required": true }
  ]
}

Lohnart numbers are placeholders.

  1. Preview, then commit the month:
tt export --from 2027-01-01 --to 2027-01-31 --mapping datev-lodas
tt export --from 2027-01-01 --to 2027-01-31 --mapping datev-lodas --commit -o datev-commit.txt   # UTF-8 copy

Then download the ANSI file of that run (run id from the commit output, GET /exports/runs or the export.completed webhook):

curl -s "$TT/exports/runs/<runId>/file" -H "Authorization: Bearer $KEY" -o datev-body.txt   # charset=windows-1252
  1. Post-process: prepend the header block (plain ASCII, so the file stays Windows-1252). Example (header content is an assumption — get the real one from your Steuerberater):
{
  printf '[Allgemein]\r\nZiel=LODAS\r\nVersion_SST=1.0\r\nBeraterNr=12345\r\nMandantenNr=99999\r\nAbrechnungszeitraum=01/2027\r\n\r\n'
  printf '[Satzbeschreibung]\r\n1;u_lod_bwd_buchung_standard;pnr#bwd;la_eigene#bwd;bs_wert_butab#bwd;kostenstelle#bwd;\r\n\r\n'
  printf '[Bewegungsdaten]\r\n'
  LC_ALL=C sed 's/^/1;/' datev-body.txt
} > datev-lodas-2027-01.txt

If you start from the UTF-8 commit copy instead, pipe the result through iconv -f UTF-8 -t WINDOWS-1252. The sed prefixes each line with the record number defined in [Satzbeschreibung]. Field codes in the example are illustrative.

  1. Send datev-lodas-2027-01.txt to the tax adviser or import it yourself. An admin locks January in the dashboard after payroll is final.

6. Sync schedule and triggering events

  • Monthly, after all weeks of the month are closed. The dashboard works per week; the API accepts the month range.
  • export.completed {runId,startsOn,endsOn,rows,mapping} and period.locked {startsOn,endsOn,kind:"month"} (admin creates webhooks). A receiver can download the committed body with GET /exports/runs/{runId}/file, run the post-processor and drop the result for the Steuerberater.
  • Committed files stay retrievable: GET /exports/runs (paginated {items, next_cursor}), GET /exports/runs/{id} (metadata, mapping_snapshot, pay_lines, warnings) and GET /exports/runs/{id}/file (the exact committed file, text/csv). A read-only key is enough.

7. Corrections, conflicts and locked periods

  • Only approved, conflict-free days are exported; pendingCount shows the rest.
  • Exported/locked days are immutable (423). Book corrections in the current month; DATEV receives them in the next file ("Korrektur im Folgemonat"). Retroactive DATEV corrections (Rückrechnung) are done in DATEV.

8. Testing checklist

  • File opens in a text editor as ANSI; umlauts (ä, ö, ü, ß) display correctly; no ? where a name or note had a character outside Latin-1.
  • Header values (Berater/Mandant/period) confirmed by the Steuerberater.
  • Test import into a DATEV test client (Testmandant) succeeds.
  • Night and Sunday hours appear as separate Lohnarten for a test week.
  • break_too_short warnings reviewed (ArbZG §4).

9. Troubleshooting

HTTP / code Message Fix
403 forbidden API key is read-only write key.
404 not_found Export mapping not found Check mapping id.
409 conflict This period was just exported; refresh before exporting again Avoid double commit.
409 conflict N day(s) in this period are not exported yet Lock refused; export remaining days.
422 invalid Export has N validation issue(s) Personalnummer too long / non-numeric, unmapped Lohnart.
422 invalid Nothing approved to export in this period Approve first.
423 locked This day is exported or locked; book a correction in an open period Correct in an open month.
DATEV import error on characters — You used the UTF-8 commit content or dashboard download: use GET /exports/runs/{id}/file or run iconv.

10. Limits and known gaps

  • No header/trailer sections in mappings; the LODAS header block needs post-processing.

  • No record-number prefix column besides const: (you can add { "header": "Satz", "source": "const:1" } as first column instead of sed).

  • No day-count (Tage) values; only hours and amounts.

  • groupBy keeps the first line's date (irrelevant for monthly Bewegungsdaten without a date column).

  • Encoding: a mapping with encoding: "windows-1252" is delivered as Windows-1252 (Latin-1) only by GET /exports/runs/{id}/file (content-type: text/csv; charset=windows-1252, no BOM). Characters outside Latin-1 (€, curly quotes, em dash) become ?. The preview/commit JSON content and the dashboard download stay UTF-8.

Planned (not in API v1.1) — export-mapping CRUD via the API.

11. Build it with Claude

Help me produce a DATEV LODAS import from TimeTracking.
1. With the TimeTracking MCP tools, run export_preview for 2027-01-01..2027-01-31 (mappingId "preset-generic") and list
   distinct hour types and employees whose employee number is not 1–5 digits.
2. Draft a datev-lodas ExportMapping JSON (see docs/developers/data-out/export-mappings.md) with a lohnart table
   (TODO numbers) and a shell post-processor that adds the [Allgemein]/[Satzbeschreibung]/[Bewegungsdaten] header and
   relies on GET /api/v1/exports/runs/{id}/file for the WINDOWS-1252 bytes. Mark every DATEV field as "verify with Steuerberater".
3. Do not commit or lock anything.