Nmbrs payroll export
Bring approved TimeTracking hours, allowances, expenses and leave into Nmbrs as hours and variable wage components, via file import or the Nmbrs API.
Last updated: · Markdown version
1. Summary
| What syncs | Approved hours per hour type, allowances, approved expenses, approved leave |
| Direction | TimeTracking → Nmbrs |
| Mechanism | CSV export (custom mapping) imported in Nmbrs, or a script that pushes rows to the Nmbrs API |
| Trigger | Per payroll period, after the weekly close |
Status: Recipe — works today with an export file or your own script. No native Nmbrs connector.
2. Mechanism
A. File import. A Nmbrs-shaped export mapping renders one line per employee per hour code (or wage component) per period. You import it in Nmbrs for the run. Assumption — Nmbrs supports importing hours and variable wage components per employee per period from Excel/CSV, keyed on employee number and a code; verify the exact template with your Nmbrs consultant.
B. API push. A script commits the TimeTracking export and writes the lines through the Nmbrs API. Assumption — Nmbrs exposes employee hour components and variable wage components per period in its API (classic SOAP services and a newer REST API); verify endpoint names, period semantics and authentication with your Nmbrs consultant.
Nmbrs distinguishes hour codes (uren) from wage components (looncomponenten, amounts). Map hours-type lines (componentType: "hours") to hour codes and money lines (expenses, allowances) to wage components. Because one mapping produces one file, use two mappings if Nmbrs needs two imports, or one mapping with both a quantity and an amount column.
3. Prerequisites
- TimeTracking admin; API key with
writescope. employee_numberin TimeTracking = Nmbrs employee number.- Nmbrs hour codes and wage component codes for each TimeTracking hour type, expense kind and leave type.
- Nmbrs company id and payroll period convention (week, 4 weeks, month). Pass the matching
startsOn/endsOn. - For option B: Nmbrs API credentials.
4. Entity mapping
| Nmbrs object / field | TimeTracking | Notes / external ID |
|---|---|---|
| Company | TimeTracking org | const: column if needed. |
| Employee number | employee_number → employeeNumber |
Only stable key today. |
| Hour code (uursoort) | hourType (normal, overtime_125, ort_*, holiday, leave_*) via map |
Lines with componentType: "hours". |
| Hours value | minutes → hours_decimal |
Decimal separator per template. |
| Wage component code | expense_<kind>, allowance_<code> via map |
Lines with componentType: "allowance". |
| Wage component value | amountCents → cents_decimal |
|
| Period / year | Export range | Not in PayLine; constant or chosen on import. |
| Cost centre / department | clientName / placementRef |
Optional. |
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 nmbrs with the Nmbrs employee id as ref.
5. Step-by-step setup
export TT=https://app.timetracking.tech/api/v1
export KEY=tt_live_… # write scope
Verify employee numbers:
tt people list(column with the employee number) orcurl -s "$TT/people" -H "Authorization: Bearer $KEY".Create the mapping yourself (admin, dashboard): open the export mappings screen (
/export/mappings, linked from Export), copypreset-genericorpreset-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 asmappingIdtoPOST /exportsandtt export --mapping. Example JSON for the editor (assumed layout — verify with your Nmbrs consultant):
{
"id": "nmbrs-uren",
"name": "Nmbrs — uren en looncomponenten",
"format": "csv",
"delimiter": ";",
"includeHeader": true,
"encoding": "utf-8",
"lineEnding": "crlf",
"groupBy": ["employeeNumber", "hourType"],
"columns": [
{ "header": "EmployeeNumber", "source": "employeeNumber" },
{ "header": "Code", "source": "hourType", "transform": { "kind": "map", "table": "codes" } },
{ "header": "Hours", "source": "minutes", "transform": { "kind": "hours_decimal", "decimals": 2, "decimalSeparator": "," } },
{ "header": "Amount", "source": "amountCents", "transform": { "kind": "cents_decimal", "decimalSeparator": "," } },
{ "header": "Type", "source": "componentType", "transform": { "kind": "upper" } }
],
"tables": {
"codes": {
"normal": "U100", "overtime_125": "U125", "overtime_150": "U150",
"ort_evening": "U210", "ort_night": "U220", "ort_saturday": "U230", "ort_sunday": "U240", "holiday": "U250",
"leave_vacation": "U300", "leave_sick": "U310",
"expense_km": "L410", "expense_public_transport": "L420", "expense_parking": "L430", "expense_meal": "L440"
}
},
"validation": [
{ "column": "EmployeeNumber", "required": true },
{ "column": "Code", "required": true }
]
}
Codes U100… are placeholders. Type renders HOURS or ALLOWANCE, so one file can be split by type before import if Nmbrs needs two imports.
- Preview, then commit (irreversible) and import:
curl -s -X POST "$TT/exports" -H "Authorization: Bearer $KEY" -H "content-type: application/json" \
-d '{"mappingId":"nmbrs-uren","startsOn":"2026-11-02","endsOn":"2026-11-29","mode":"preview"}' \
| jq '.data | {ok, issues, warnings, pendingCount, rows: (.rows|length)}'
tt export --from 2026-11-02 --to 2026-11-29 --mapping nmbrs-uren --commit -o nmbrs-p11.csv
Import
nmbrs-p11.csvin Nmbrs for the matching period. For option B, parsedata.contentfrom the commit response (or download it later withGET /exports/runs/{runId}/file) and post each line to the Nmbrs API (see the Loket guide for a TypeScript parsing sketch: Loket.nl).After Nmbrs processed the run, an admin locks the period in the dashboard.
6. Sync schedule and triggering events
- Per Nmbrs payroll period. For 4-weekly payroll pass the 28-day range; for monthly the calendar month.
export.completedandperiod.lockedwebhooks signal progress (an admin creates them in the dashboard, or via the API with an admin user token or anadmin-scope key). Onexport.completed, download the file withGET /exports/runs/{runId}/file. See Webhook events.- Committed files stay retrievable:
GET /exports/runs(paginated{items, next_cursor}),GET /exports/runs/{id}(metadata,mapping_snapshot,pay_lines,warnings) andGET /exports/runs/{id}/file(the exact committed file,text/csv). A read-only key is enough.
7. Corrections, conflicts and locked periods
- Unapproved and conflicted days stay out of the file (
pendingCount). - Exported and locked days are immutable (
423). Book corrections in an open period. - If an Nmbrs import is rejected after commit, fix it on the Nmbrs side using the saved file. Do not record the hours again in TimeTracking.
8. Testing checklist
- All codes mapped; preview
issues: []. - Hours vs amounts land in the correct Nmbrs component type.
- Test import into a Nmbrs test run; totals per employee equal the TimeTracking overview.
- Leave hours are not counted twice if leave is also registered in Nmbrs.
9. Troubleshooting
| HTTP / code | Message | Fix |
|---|---|---|
401 unauthenticated |
Invalid or revoked API key | New key. |
403 forbidden |
API key is read-only | write key for exports. |
404 not_found |
Export mapping not found | Check the 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 and fix missing numbers/codes. |
422 invalid |
Nothing approved to export in this period | Approve days 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 | Retry after 60 s. |
10. Limits and known gaps
Mappings are edited by admins in the dashboard only; there is no mapping API.
One mapping = one file; hours and wage components share it unless you use two mappings.
groupBykeeps the first line's date.Leave double-counting risk if leave is managed in both systems: pick one source.
Encoding: a mapping with
encoding: "windows-1252"is delivered as Windows-1252 (Latin-1) only byGET /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 JSONcontentand 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 connect TimeTracking to Nmbrs.
1. Using the TimeTracking MCP tools, list people and flag any without an employee number.
2. Run export_preview with mappingId "preset-generic" for 2026-11-02..2026-11-29 and list every distinct hour_type.
3. Draft a Nmbrs ExportMapping JSON (schema: docs/developers/data-out/export-mappings.md) with a codes table that
has one TODO code per hour_type; split hours vs amounts using componentType.
4. Do not commit any export. Mark every Nmbrs-specific field as "verify with Nmbrs consultant".