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.

Visma | Raet (Youforce) payroll export

Deliver approved TimeTracking hours, allowances, expenses and leave to Visma | Raet Youforce payroll as a variable-data file.

Last updated: · Markdown version

1. Summary

What syncs Approved hours per hour type, allowances, approved expenses, approved leave
Direction TimeTracking → Visma | Raet Youforce payroll
Mechanism CSV export with a custom mapping, delivered as a variable-data (mutaties) file
Trigger Per payroll period

Status: Recipe — works today with a file that matches your Youforce variable-data layout, built by an admin as a custom export mapping in the dashboard. No native connector.

2. Mechanism

TimeTracking renders one line per employee per wage component per period with a custom export mapping. The file is uploaded (or delivered by your payroll administrator) into Youforce as variable data.

Assumption — Youforce payroll accepts variable data ("variabele gegevens"/mutaties) through a file interface with a fixed record layout per customer contract (sometimes fixed-width, sometimes delimited, often with employer and period fields). The exact layout, field lengths and code lists are customer-specific; verify with your Visma | Raet consultant.

Fixed-width layouts can be approximated with pad transforms and delimiter |, but TimeTracking cannot emit true fixed-width records without a delimiter. If Youforce requires fixed width, post-process the file (see section 5).

3. Prerequisites

  • TimeTracking admin; API key with write scope.
  • employee_number in TimeTracking = Youforce personnel number (often with leading zeros: use pad).
  • Youforce wage component codes (looncomponenten) per hour type, expense kind and leave type.
  • Employer number and the record layout from your Visma | Raet consultant.

4. Entity mapping

Youforce object / field TimeTracking Notes / external ID
Werkgeversnummer TimeTracking org const: column.
Personeelsnummer employee_number → employeeNumber pad to the required length. Only stable key today.
Dienstverbandvolgnummer Not modelled const:1 if always the first employment. Assumption — verify.
Looncomponent hourType via map
Waarde (hours/units) minutes → hours_decimal Some layouts want hundredths without separator: post-process.
Bedrag amountCents cents_decimal, or raw cents via plain source (amountCents without transform).
Ingangsdatum / periode date (YYYYMMDD) or constant

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.

5. Step-by-step setup

export TT=https://app.timetracking.tech/api/v1
export KEY=tt_live_…        # write scope
  1. 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 Visma | Raet consultant):
{
  "id": "youforce-mutaties",
  "name": "Visma | Raet Youforce — variabele gegevens",
  "format": "txt",
  "delimiter": ";",
  "includeHeader": false,
  "encoding": "utf-8",
  "lineEnding": "crlf",
  "groupBy": ["employeeNumber", "hourType"],
  "columns": [
    { "header": "Werkgever", "source": "const:000123" },
    { "header": "Personeelsnummer", "source": "employeeNumber", "transform": { "kind": "pad", "length": 8, "char": "0" } },
    { "header": "Dienstverband", "source": "const:1" },
    { "header": "Looncomponent", "source": "hourType", "transform": { "kind": "map", "table": "lc" } },
    { "header": "Waarde", "source": "minutes", "transform": { "kind": "hours_decimal", "decimals": 2, "decimalSeparator": "," } },
    { "header": "Bedrag", "source": "amountCents", "transform": { "kind": "cents_decimal", "decimalSeparator": "," } },
    { "header": "Ingangsdatum", "source": "date", "transform": { "kind": "date", "format": "YYYYMMDD" } }
  ],
  "tables": { "lc": { "normal": "0100", "overtime_125": "0125", "ort_sunday": "0240", "expense_km": "0410", "leave_vacation": "0300" } },
  "validation": [
    { "column": "Personeelsnummer", "required": true, "pattern": "^\\d{8}$" },
    { "column": "Looncomponent", "required": true }
  ]
}

Codes are placeholders.

  1. Preview and commit:
tt export --from 2026-11-01 --to 2026-11-30 --mapping youforce-mutaties
tt export --from 2026-11-01 --to 2026-11-30 --mapping youforce-mutaties --commit -o youforce-2026-11.txt
  1. If Youforce needs fixed width, convert after commit. Example (field widths are assumptions):
awk -F';' 'BEGIN{OFS=""} {printf "%-6s%-8s%-2s%-4s%10s\r\n", $1,$2,$3,$4,$5}' youforce-2026-11.txt > youforce-2026-11.fixed
  1. Deliver the file to Youforce. After processing, an admin locks the period in the dashboard.

6. Sync schedule and triggering events

  • Monthly (or per Youforce payroll period).
  • export.completed and period.locked webhooks (admin creates them). On export.completed, download the file with GET /exports/runs/{runId}/file, post-process and deliver. See Webhook events.
  • 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.
  • Exported/locked days are immutable (423). Book corrections in an open period.
  • Youforce rejections are fixed on the Youforce side from the saved file.

8. Testing checklist

  • Layout validated by Visma | Raet on a sample file.
  • Personnel numbers padded correctly; all codes mapped.
  • Test run totals equal TimeTracking totals.

9. Troubleshooting

HTTP / code Message Fix
401 unauthenticated Invalid or revoked API key New key.
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.
422 invalid Export has N validation issue(s) Preview; often unpadded numbers or unmapped codes.
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 period.
429 rate_limited Rate limit … exceeded Wait 60 s.

10. Limits and known gaps

  • No true fixed-width output; post-process if required.

  • Mappings are edited by admins in the dashboard only; there is no mapping API.

  • If Youforce needs ANSI, set "encoding": "windows-1252" in the mapping and download the run with GET /exports/runs/{id}/file (or convert the UTF-8 copy with iconv -f UTF-8 -t WINDOWS-1252).

  • groupBy keeps the first line's date.

  • 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

I have a Visma | Raet Youforce variable-data record layout (pasted below). Using
docs/developers/data-out/export-mappings.md, write a TimeTracking ExportMapping JSON that matches it as closely as
possible, plus an awk or Node post-processor for anything the mapping cannot express (fixed width, ANSI encoding,
header/trailer records). List every assumption that I must verify with our Visma | Raet consultant.
<paste layout here>